From 780319a75ba1ce24c5a4cec37051d9ad0a4f90de Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sat, 23 Aug 2025 09:01:38 -0400 Subject: [PATCH 01/11] stabilize unstable `rwlock_downgrade` feature Signed-off-by: Connor Tsui --- library/std/src/sync/nonpoison/rwlock.rs | 5 +---- library/std/src/sync/poison/rwlock.rs | 6 +----- library/std/tests/sync/lib.rs | 1 - 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/library/std/src/sync/nonpoison/rwlock.rs b/library/std/src/sync/nonpoison/rwlock.rs index eb0aef99cc1e7..0e0d7e058b077 100644 --- a/library/std/src/sync/nonpoison/rwlock.rs +++ b/library/std/src/sync/nonpoison/rwlock.rs @@ -638,7 +638,6 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// /// ``` /// #![feature(nonpoison_rwlock)] - /// #![feature(rwlock_downgrade)] /// /// use std::sync::nonpoison::{RwLock, RwLockWriteGuard}; /// @@ -657,7 +656,6 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// /// ``` /// #![feature(nonpoison_rwlock)] - /// #![feature(rwlock_downgrade)] /// /// use std::sync::Arc; /// use std::sync::nonpoison::{RwLock, RwLockWriteGuard}; @@ -690,8 +688,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// # let final_check = rw.read(); /// # assert_eq!(*final_check, 3); /// ``` - #[unstable(feature = "rwlock_downgrade", issue = "128203")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + #[unstable(feature = "nonpoison_rwlock", issue = "134645")] pub fn downgrade(s: Self) -> RwLockReadGuard<'rwlock, T> { let lock = s.lock; diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 0a463f3f9c7e3..1d52992ec2e0a 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -813,8 +813,6 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// `downgrade` takes ownership of the `RwLockWriteGuard` and returns a [`RwLockReadGuard`]. /// /// ``` - /// #![feature(rwlock_downgrade)] - /// /// use std::sync::{RwLock, RwLockWriteGuard}; /// /// let rw = RwLock::new(0); @@ -831,8 +829,6 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// thread calling `downgrade` and any reads it performs after downgrading. /// /// ``` - /// #![feature(rwlock_downgrade)] - /// /// use std::sync::{Arc, RwLock, RwLockWriteGuard}; /// /// let rw = Arc::new(RwLock::new(1)); @@ -863,7 +859,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { /// # let final_check = rw.read().unwrap(); /// # assert_eq!(*final_check, 3); /// ``` - #[unstable(feature = "rwlock_downgrade", issue = "128203")] + #[stable(feature = "rwlock_downgrade", since = "CURRENT_RUSTC_VERSION")] pub fn downgrade(s: Self) -> RwLockReadGuard<'rwlock, T> { let lock = s.lock; diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index f874c2ba38951..d44a4d6935743 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -4,7 +4,6 @@ #![feature(once_cell_try)] #![feature(lock_value_accessors)] #![feature(reentrant_lock)] -#![feature(rwlock_downgrade)] #![feature(std_internals)] #![feature(sync_nonpoison)] #![feature(nonpoison_mutex)] From fa9162d06fe101ecb5301146281882341fded5ec Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Tue, 7 Oct 2025 10:56:02 -0400 Subject: [PATCH 02/11] Allow printing a fully-qualified path in `def_path_str` Previously, the local crate would always be printed as a leading `crate::`. Allow resolving it to the crate name instead. This allows printing a fully qualified path with: ```rust let qualified_name = with_no_visible_paths!(with_resolve_crate_name!( with_no_trimmed_paths!(tcx.def_path_str(def_id)) )); ``` I found this useful for an out-of-tree rustc-driver. I do not currently have a usecase in mind upstream; I'm ok if you don't want this PR for that reason. This does not currently have tests. I am not aware of an easy way to test def-id printing, since it requires having access to a TyCtxt. --- compiler/rustc_middle/src/ty/print/pretty.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 8f7c8170f7adf..978345d13f1e9 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -31,6 +31,7 @@ use crate::ty::{ thread_local! { static FORCE_IMPL_FILENAME_LINE: Cell = const { Cell::new(false) }; + static SHOULD_PREFIX_WITH_CRATE_NAME: Cell = const { Cell::new(false) }; static SHOULD_PREFIX_WITH_CRATE: Cell = const { Cell::new(false) }; static NO_TRIMMED_PATH: Cell = const { Cell::new(false) }; static FORCE_TRIMMED_PATH: Cell = const { Cell::new(false) }; @@ -98,7 +99,18 @@ define_helper!( /// cycle errors, this can result in extra or suboptimal error output, /// so this variable disables that check. fn with_forced_impl_filename_line(ForcedImplGuard, FORCE_IMPL_FILENAME_LINE); + /// Adds the crate name prefix to paths where appropriate. + /// Unlike `with_crate_prefix`, this unconditionally uses `tcx.crate_name` instead of sometimes + /// using `crate::` for local items. + /// + /// Overrides `with_crate_prefix`. + + // This function is not currently used in-tree, but it's used by a downstream rustc-driver in + // Ferrocene. Please check with them before removing it. + fn with_resolve_crate_name(CrateNamePrefixGuard, SHOULD_PREFIX_WITH_CRATE_NAME); /// Adds the `crate::` prefix to paths where appropriate. + /// + /// Ignored if `with_resolve_crate_name` is active. fn with_crate_prefix(CratePrefixGuard, SHOULD_PREFIX_WITH_CRATE); /// Prevent path trimming if it is turned on. Path trimming affects `Display` impl /// of various rustc types, for example `std::vec::Vec` would be trimmed to `Vec`, @@ -2313,7 +2325,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.empty_path = true; - if cnum == LOCAL_CRATE { + if cnum == LOCAL_CRATE && !with_resolve_crate_name() { if self.tcx.sess.at_least_rust_2018() { // We add the `crate::` keyword on Rust 2018, only when desired. if with_crate_prefix() { From 4dfbf116443c8cd16135a95c15bae60857bd891a Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 14 Oct 2025 11:31:32 +0200 Subject: [PATCH 03/11] cleanup ErrorGuaranteed handling --- .../rustc_hir_analysis/src/check/wfcheck.rs | 4 +- .../rustc_hir_analysis/src/coherence/mod.rs | 8 ++-- .../rustc_hir_analysis/src/impl_wf_check.rs | 37 +++++-------------- compiler/rustc_hir_typeck/src/expr.rs | 4 +- 4 files changed, 16 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 7921e34ae4bd5..6e537c668843c 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1651,9 +1651,7 @@ fn check_method_receiver<'tcx>( // If the receiver already has errors reported, consider it valid to avoid // unnecessary errors (#58712). - if receiver_ty.references_error() { - return Ok(()); - } + receiver_ty.error_reported()?; let arbitrary_self_types_level = if tcx.features().arbitrary_self_types_pointers() { Some(ArbitrarySelfTypesLevel::WithPointers) diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index ed8f78216ff2c..fbb442440beae 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -39,9 +39,7 @@ fn check_impl<'tcx>( // Skip impls where one of the self type is an error type. // This occurs with e.g., resolve failures (#30589). - if trait_ref.references_error() { - return Ok(()); - } + trait_ref.error_reported()?; enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id, trait_def) .and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id, trait_def)) @@ -188,9 +186,9 @@ fn check_object_overlap<'tcx>( ) -> Result<(), ErrorGuaranteed> { let trait_def_id = trait_ref.def_id; - if trait_ref.references_error() { + if let Err(guar) = trait_ref.error_reported() { debug!("coherence: skipping impl {:?} with error {:?}", impl_def_id, trait_ref); - return Ok(()); + return Err(guar); } // check for overlap with the automatic `impl Trait for dyn Trait` diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index cbdc501291bc8..19ba166fa42a8 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -76,20 +76,10 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( impl_def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity(); - if impl_self_ty.references_error() { - // Don't complain about unconstrained type params when self ty isn't known due to errors. - // (#36836) - tcx.dcx().span_delayed_bug( - tcx.def_span(impl_def_id), - format!( - "potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}", - ), - ); - // This is super fishy, but our current `rustc_hir_analysis::check_crate` pipeline depends on - // `type_of` having been called much earlier, and thus this value being read from cache. - // Compilation must continue in order for other important diagnostics to keep showing up. - return Ok(()); - } + + // Don't complain about unconstrained type params when self ty isn't known due to errors. + // (#36836) + impl_self_ty.error_reported()?; let impl_generics = tcx.generics_of(impl_def_id); let impl_predicates = tcx.predicates_of(impl_def_id); @@ -174,20 +164,11 @@ pub(crate) fn enforce_impl_non_lifetime_params_are_constrained( impl_def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity(); - if impl_self_ty.references_error() { - // Don't complain about unconstrained type params when self ty isn't known due to errors. - // (#36836) - tcx.dcx().span_delayed_bug( - tcx.def_span(impl_def_id), - format!( - "potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}", - ), - ); - // This is super fishy, but our current `rustc_hir_analysis::check_crate` pipeline depends on - // `type_of` having been called much earlier, and thus this value being read from cache. - // Compilation must continue in order for other important diagnostics to keep showing up. - return Ok(()); - } + + // Don't complain about unconstrained type params when self ty isn't known due to errors. + // (#36836) + impl_self_ty.error_reported()?; + let impl_generics = tcx.generics_of(impl_def_id); let impl_predicates = tcx.predicates_of(impl_def_id); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 1aaf02646c794..026bc35cfa065 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -685,9 +685,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let ty = self.check_expr_with_expectation_and_needs(oprnd, hint, Needs::maybe_mut_place(mutbl)); + if let Err(guar) = ty.error_reported() { + return Ty::new_error(self.tcx, guar); + } match kind { - _ if ty.references_error() => Ty::new_misc_error(self.tcx), hir::BorrowKind::Raw => { self.check_named_place_expr(oprnd); Ty::new_ptr(self.tcx, ty, mutbl) From 3941b4299309df315acbdb97be3520c670558860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 14 Oct 2025 15:21:27 +0200 Subject: [PATCH 04/11] return spans out of is_doc_comment to reduce reliance on .span() on attrs --- compiler/rustc_ast/src/attr/mod.rs | 11 ++++++----- compiler/rustc_attr_parsing/src/attributes/util.rs | 3 ++- compiler/rustc_hir/src/hir.rs | 10 +++++++--- compiler/rustc_lint/src/builtin.rs | 2 +- compiler/rustc_query_system/src/ich/impls_syntax.rs | 2 +- compiler/rustc_resolve/src/rustdoc.rs | 3 +-- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/passes/propagate_doc_cfg.rs | 2 +- .../clippy/clippy_lints/src/four_forward_slashes.rs | 2 +- .../clippy_lints/src/undocumented_unsafe_blocks.rs | 2 +- 10 files changed, 22 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 6ada93b4c89b9..901b645b8c4ef 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -86,10 +86,10 @@ impl AttributeExt for Attribute { /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example). /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not /// a doc comment) will return `false`. - fn is_doc_comment(&self) -> bool { + fn is_doc_comment(&self) -> Option { match self.kind { - AttrKind::Normal(..) => false, - AttrKind::DocComment(..) => true, + AttrKind::Normal(..) => None, + AttrKind::DocComment(..) => Some(self.span), } } @@ -776,7 +776,7 @@ pub trait AttributeExt: Debug { /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example). /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not /// a doc comment) will return `false`. - fn is_doc_comment(&self) -> bool; + fn is_doc_comment(&self) -> Option; #[inline] fn has_name(&self, name: Symbol) -> bool { @@ -863,8 +863,9 @@ impl Attribute { AttributeExt::path_matches(self, name) } + // on ast attributes we return a bool since that's what most code already expects pub fn is_doc_comment(&self) -> bool { - AttributeExt::is_doc_comment(self) + AttributeExt::is_doc_comment(self).is_some() } #[inline] diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 62b72798e9699..520fd9da7c2ab 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -28,7 +28,8 @@ pub fn parse_version(s: Symbol) -> Option { } pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { - attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) + attr.is_doc_comment().is_some() + || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) } pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index bc1c47e95c3ae..7886cce5cdd46 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1304,8 +1304,12 @@ impl AttributeExt for Attribute { } #[inline] - fn is_doc_comment(&self) -> bool { - matches!(self, Attribute::Parsed(AttributeKind::DocComment { .. })) + fn is_doc_comment(&self) -> Option { + if let Attribute::Parsed(AttributeKind::DocComment { span, .. }) = self { + Some(*span) + } else { + None + } } #[inline] @@ -1425,7 +1429,7 @@ impl Attribute { } #[inline] - pub fn is_doc_comment(&self) -> bool { + pub fn is_doc_comment(&self) -> Option { AttributeExt::is_doc_comment(self) } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8a525eb11f7bb..183b23418745b 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -391,7 +391,7 @@ pub struct MissingDoc; impl_lint_pass!(MissingDoc => [MISSING_DOCS]); fn has_doc(attr: &hir::Attribute) -> bool { - if attr.is_doc_comment() { + if attr.is_doc_comment().is_some() { return true; } diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 1dcd5d9058f4e..044b97c2fea19 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -22,7 +22,7 @@ impl<'a> HashStable> for [hir::Attribute] { let filtered: SmallVec<[&hir::Attribute; 8]> = self .iter() .filter(|attr| { - !attr.is_doc_comment() + attr.is_doc_comment().is_none() // FIXME(jdonszelmann) have a better way to handle ignored attrs && !attr.ident().is_some_and(|ident| hcx.is_ignored_attr(ident.name)) }) diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 804792c6f286d..944311a85824f 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -214,8 +214,7 @@ pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>( for (attr, item_id) in attrs { if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { let doc = beautify_doc_string(doc_str, comment_kind); - let (span, kind, from_expansion) = if attr.is_doc_comment() { - let span = attr.span(); + let (span, kind, from_expansion) = if let Some(span) = attr.is_doc_comment() { (span, DocFragmentKind::SugaredDoc, span.from_expansion()) } else { let attr_span = attr.span(); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 4fd8d245089e7..46717367d83b3 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2719,7 +2719,7 @@ fn add_without_unwanted_attributes<'hir>( import_parent: Option, ) { for attr in new_attrs { - if attr.is_doc_comment() { + if attr.is_doc_comment().is_some() { attrs.push((Cow::Borrowed(attr), import_parent)); continue; } diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index d5b20f2b9410c..c0b48ab51c7eb 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -65,7 +65,7 @@ fn filter_non_cfg_tokens_from_list(args_tokens: &TokenStream) -> Vec /// it and put them into `attrs`. fn add_only_cfg_attributes(attrs: &mut Vec, new_attrs: &[Attribute]) { for attr in new_attrs { - if attr.is_doc_comment() { + if attr.is_doc_comment().is_some() { continue; } let mut attr = attr.clone(); diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs index a7b0edeb7991f..5a0cee40a1551 100644 --- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs +++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { .tcx .hir_attrs(item.hir_id()) .iter() - .filter(|i| i.is_doc_comment()) + .filter(|i| i.is_doc_comment().is_some()) .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span())); let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else { return; diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index ba0d4de5f3b3b..751e9b0034277 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -475,7 +475,7 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { span.to(cx.tcx.hir_attrs(hir_id).iter().fold(span, |acc, attr| { - if attr.is_doc_comment() { + if attr.is_doc_comment().is_some() { return acc; } acc.to(attr.span()) From 0935df7829c466f8ef94248429095ea79dc0fa73 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 15 Oct 2025 09:05:09 +0800 Subject: [PATCH 05/11] Fix ICE when using contracts on async functions --- compiler/rustc_builtin_macros/src/contracts.rs | 8 ++++++++ .../contracts/async-fn-contract-ice-145333.rs | 10 ++++++++++ .../async-fn-contract-ice-145333.stderr | 17 +++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 tests/ui/contracts/async-fn-contract-ice-145333.rs create mode 100644 tests/ui/contracts/async-fn-contract-ice-145333.stderr diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index 6a24af361fe78..13c63268e1e87 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -71,6 +71,14 @@ fn expand_contract_clause( .span_err(attr_span, "contract annotations can only be used on functions")); } + // Contracts are not yet supported on async/gen functions + if new_tts.iter().any(|tt| is_kw(tt, kw::Async) || is_kw(tt, kw::Gen)) { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations are not yet supported on async or gen functions", + )); + } + // Found the `fn` keyword, now find either the `where` token or the function body. let next_tt = loop { let Some(tt) = cursor.next() else { diff --git a/tests/ui/contracts/async-fn-contract-ice-145333.rs b/tests/ui/contracts/async-fn-contract-ice-145333.rs new file mode 100644 index 0000000000000..a6de8a786af96 --- /dev/null +++ b/tests/ui/contracts/async-fn-contract-ice-145333.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --crate-type=lib +//@ edition: 2021 +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete + +#[core::contracts::ensures(|ret| *ret)] +//~^ ERROR contract annotations are not yet supported on async or gen functions +async fn _always_true(b: bool) -> bool { + b +} diff --git a/tests/ui/contracts/async-fn-contract-ice-145333.stderr b/tests/ui/contracts/async-fn-contract-ice-145333.stderr new file mode 100644 index 0000000000000..77f5379e6fb54 --- /dev/null +++ b/tests/ui/contracts/async-fn-contract-ice-145333.stderr @@ -0,0 +1,17 @@ +error: contract annotations are not yet supported on async or gen functions + --> $DIR/async-fn-contract-ice-145333.rs:6:1 + | +LL | #[core::contracts::ensures(|ret| *ret)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/async-fn-contract-ice-145333.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 1 previous error; 1 warning emitted + From 491bc5008c59a7ee16e96a3e4e7d2b52e108902e Mon Sep 17 00:00:00 2001 From: ltdk Date: Tue, 14 Oct 2025 20:39:39 -0400 Subject: [PATCH 06/11] const mem::drop --- library/core/src/mem/mod.rs | 9 +++- .../traits/next-solver/alias-bound-unsound.rs | 6 ++- .../next-solver/alias-bound-unsound.stderr | 46 +++++++++---------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index c484551187cc3..a537269284773 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -6,7 +6,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::alloc::Layout; -use crate::marker::DiscriminantKind; +use crate::marker::{Destruct, DiscriminantKind}; use crate::panic::const_assert; use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; @@ -958,8 +958,13 @@ pub const fn replace(dest: &mut T, src: T) -> T { /// [`RefCell`]: crate::cell::RefCell #[inline] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_destruct", issue = "133214")] #[rustc_diagnostic_item = "mem_drop"] -pub fn drop(_x: T) {} +pub const fn drop(_x: T) +where + T: [const] Destruct, +{ +} /// Bitwise-copies a value. /// diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.rs b/tests/ui/traits/next-solver/alias-bound-unsound.rs index 0236826c3ed29..57fc88d87cf65 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.rs +++ b/tests/ui/traits/next-solver/alias-bound-unsound.rs @@ -4,6 +4,10 @@ #![feature(trivial_bounds)] +// we use identity instead of drop because the presence of [const] Destruct means that there +// are additional bounds on the function, which result in additional errors +use std::convert::identity; + trait Foo { type Item: Copy where @@ -21,7 +25,7 @@ impl Foo for () { fn main() { let x = String::from("hello, world"); - drop(<() as Foo>::copy_me(&x)); + let _ = identity(<() as Foo>::copy_me(&x)); //~^ ERROR overflow evaluating the requirement `String <: <() as Foo>::Item` //~| ERROR overflow evaluating the requirement `<() as Foo>::Item well-formed` //~| ERROR overflow evaluating the requirement `&<() as Foo>::Item well-formed` diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.stderr b/tests/ui/traits/next-solver/alias-bound-unsound.stderr index 7e3737d120bf4..1079c27fa815f 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/next-solver/alias-bound-unsound.stderr @@ -1,11 +1,11 @@ error[E0275]: overflow evaluating the requirement `String: Copy` - --> $DIR/alias-bound-unsound.rs:18:38 + --> $DIR/alias-bound-unsound.rs:22:38 | LL | type Item = String where String: Copy; | ^^^^ | note: the requirement `String: Copy` appears on the `impl`'s associated type `Item` but not on the corresponding trait's associated type - --> $DIR/alias-bound-unsound.rs:8:10 + --> $DIR/alias-bound-unsound.rs:12:10 | LL | trait Foo { | --- in this trait @@ -13,50 +13,50 @@ LL | type Item: Copy | ^^^^ this trait's associated type doesn't have the requirement `String: Copy` error[E0275]: overflow evaluating the requirement `String <: <() as Foo>::Item` - --> $DIR/alias-bound-unsound.rs:24:31 + --> $DIR/alias-bound-unsound.rs:28:43 | -LL | drop(<() as Foo>::copy_me(&x)); - | ^^ +LL | let _ = identity(<() as Foo>::copy_me(&x)); + | ^^ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:24:10 + --> $DIR/alias-bound-unsound.rs:28:22 | -LL | drop(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = identity(<() as Foo>::copy_me(&x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:24:10 + --> $DIR/alias-bound-unsound.rs:28:22 | -LL | drop(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = identity(<() as Foo>::copy_me(&x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0275]: overflow evaluating the requirement `&<() as Foo>::Item well-formed` - --> $DIR/alias-bound-unsound.rs:24:31 + --> $DIR/alias-bound-unsound.rs:28:43 | -LL | drop(<() as Foo>::copy_me(&x)); - | ^^ +LL | let _ = identity(<() as Foo>::copy_me(&x)); + | ^^ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item well-formed` - --> $DIR/alias-bound-unsound.rs:24:10 + --> $DIR/alias-bound-unsound.rs:28:22 | -LL | drop(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = identity(<() as Foo>::copy_me(&x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:24:10 + --> $DIR/alias-bound-unsound.rs:28:22 | -LL | drop(<() as Foo>::copy_me(&x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = identity(<() as Foo>::copy_me(&x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:24:31 + --> $DIR/alias-bound-unsound.rs:28:43 | -LL | drop(<() as Foo>::copy_me(&x)); - | ^^ +LL | let _ = identity(<() as Foo>::copy_me(&x)); + | ^^ error: aborting due to 8 previous errors From 07df2adbdfd897a6dad194d06cc6b43db78cb2fb Mon Sep 17 00:00:00 2001 From: zhetaicheleba Date: Wed, 15 Oct 2025 14:23:28 +0800 Subject: [PATCH 07/11] Fix some comments Signed-off-by: zhetaicheleba --- compiler/rustc_attr_parsing/src/attributes/mod.rs | 2 +- compiler/rustc_data_structures/src/graph/scc/mod.rs | 2 +- compiler/rustc_sanitizers/src/cfi/typeid/mod.rs | 2 +- .../src/error_reporting/traits/on_unimplemented.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 8dbf4c0ef32ef..639c75d7c5e47 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -302,7 +302,7 @@ pub(crate) trait CombineAttributeParser: 'static { type Item; /// A function that converts individual items (of type [`Item`](Self::Item)) into the final attribute. /// - /// For example, individual representations fomr `#[repr(...)]` attributes into an `AttributeKind::Repr(x)`, + /// For example, individual representations from `#[repr(...)]` attributes into an `AttributeKind::Repr(x)`, /// where `x` is a vec of these individual reprs. const CONVERT: ConvertFn; diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 518817ea0f53a..1882e6e835a4e 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -289,7 +289,7 @@ enum NodeState { #[derive(Copy, Clone, Debug)] enum WalkReturn { /// The walk found a cycle, but the entire component is not known to have - /// been fully walked yet. We only know the minimum depth of this + /// been fully walked yet. We only know the minimum depth of this /// component in a minimum spanning tree of the graph. This component /// is tentatively represented by the state of the first node of this /// cycle we met, which is at `min_depth`. diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs index 5d1ee3d797885..3de0c31212568 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs @@ -21,7 +21,7 @@ bitflags! { const GENERALIZE_REPR_C = 2; /// Normalizes integers for compatibility with Clang /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM - /// CFI and KCFI support. + /// CFI and KCFI support. const NORMALIZE_INTEGERS = 4; /// Do not perform self type erasure for attaching a secondary type id to methods with their /// concrete self so they can be used as function pointers. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 00c123981e1c0..b7d470df0cf44 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -309,7 +309,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub struct OnUnimplementedFormatString { /// Symbol of the format string, i.e. `"content"` symbol: Symbol, - ///The span of the format string, i.e. `"content"` + /// The span of the format string, i.e. `"content"` span: Span, is_diagnostic_namespace_variant: bool, } From de67ea37a7f81a7209568f841e860a35d1d806c6 Mon Sep 17 00:00:00 2001 From: apiraino Date: Thu, 9 Oct 2025 16:41:07 +0200 Subject: [PATCH 08/11] Update t-compiler beta nomination Zulip msg --- triagebot.toml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index a04f8d2807235..d7e86505dd913 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -742,13 +742,15 @@ message_on_reopen = "PR #{number} has been reopened. Pinging @*T-rustdoc*." [notify-zulip."beta-nominated".compiler] required_labels = ["T-compiler"] zulip_stream = 474880 # #t-compiler/backports -topic = "#{number}: beta-nominated" +topic = "#{number}: beta-backport nomination" message_on_add = [ """\ -@**channel** PR #{number} "{title}" has been nominated for beta backport. +PR #{number} "{title}" fixes a regression. +{recipients}, please evaluate nominating this PR for backport. +The following poll is a vibe-check and not binding. """, """\ -/poll Approve beta backport of #{number}? +/poll Should #{number} be beta backported? approve decline don't know From 68080de4e39bdfa41fa0657f94579860e19cb2f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 15 Oct 2025 15:47:44 +0000 Subject: [PATCH 09/11] ignore boring locals when explaining borrow due to drop Polonius liveness has to contain boring locals, and we ignore them in diagnostics to match NLL diagnostics, since they doesn't contain boring locals. We ignored these when explaining why a loan contained a point due to a use of a live var, but not when it contained a point due to a drop of a live var. --- compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs | 4 ++-- .../polonius/lending-iterator-sanity-checks.polonius.stderr | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 638d89f5bcbdb..6bcfa9d8bf9a7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -687,7 +687,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } - Some(Cause::DropVar(local, location)) => { + Some(Cause::DropVar(local, location)) if !is_local_boring(local) => { let mut should_note_order = false; if self.local_name(local).is_some() && let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place @@ -705,7 +705,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } - Some(Cause::LiveVar(..)) | None => { + Some(Cause::LiveVar(..) | Cause::DropVar(..)) | None => { // Here, under NLL: no cause was found. Under polonius: no cause was found, or a // boring local was found, which we ignore like NLLs do to match its diagnostics. if let Some(region) = self.to_error_region_vid(borrow_region_vid) { diff --git a/tests/ui/nll/polonius/lending-iterator-sanity-checks.polonius.stderr b/tests/ui/nll/polonius/lending-iterator-sanity-checks.polonius.stderr index bf8546b63bf30..fa201b89048af 100644 --- a/tests/ui/nll/polonius/lending-iterator-sanity-checks.polonius.stderr +++ b/tests/ui/nll/polonius/lending-iterator-sanity-checks.polonius.stderr @@ -1,13 +1,15 @@ error[E0499]: cannot borrow `*t` as mutable more than once at a time --> $DIR/lending-iterator-sanity-checks.rs:19:19 | +LL | fn use_live(t: &mut T) -> Option<(T::Item<'_>, T::Item<'_>)> { + | - let's call the lifetime of this reference `'1` LL | let Some(i) = t.next() else { return None }; | - first mutable borrow occurs here LL | let Some(j) = t.next() else { return None }; | ^ second mutable borrow occurs here ... -LL | } - | - first borrow might be used here, when `i` is dropped and runs the destructor for type `::Item<'_>` +LL | Some((i, j)) + | ------------ returning this value requires that `*t` is borrowed for `'1` error[E0499]: cannot borrow `*t` as mutable more than once at a time --> $DIR/lending-iterator-sanity-checks.rs:31:13 From 804af9945702fd5db27a4a95c412a8474f0f97cf Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 15 Oct 2025 10:24:22 +0200 Subject: [PATCH 10/11] miri: use allocator_shim_contents codegen helper --- compiler/rustc_ast/src/expand/allocator.rs | 17 ++ compiler/rustc_codegen_llvm/src/allocator.rs | 16 +- compiler/rustc_codegen_ssa/src/base.rs | 1 + src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/machine.rs | 39 +++ src/tools/miri/src/shims/alloc.rs | 115 +++++++-- src/tools/miri/src/shims/foreign_items.rs | 235 +++++-------------- 7 files changed, 218 insertions(+), 206 deletions(-) diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index c200921e5f807..332ad50d927fa 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -31,10 +31,23 @@ pub enum AllocatorTy { Usize, } +/// Some allocator methods are known to the compiler: they act more like +/// intrinsics/language primitives than library-defined functions. +/// FIXME: ideally this would be derived from attributes like `#[rustc_allocator]`, +/// so we don't have two sources of truth. +#[derive(Copy, Clone, Debug)] +pub enum SpecialAllocatorMethod { + Alloc, + AllocZeroed, + Dealloc, + Realloc, +} + /// A method that will be codegened in the allocator shim. #[derive(Copy, Clone)] pub struct AllocatorMethod { pub name: Symbol, + pub special: Option, pub inputs: &'static [AllocatorMethodInput], pub output: AllocatorTy, } @@ -47,11 +60,13 @@ pub struct AllocatorMethodInput { pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ AllocatorMethod { name: sym::alloc, + special: Some(SpecialAllocatorMethod::Alloc), inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }], output: AllocatorTy::ResultPtr, }, AllocatorMethod { name: sym::dealloc, + special: Some(SpecialAllocatorMethod::Dealloc), inputs: &[ AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr }, AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }, @@ -60,6 +75,7 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ }, AllocatorMethod { name: sym::realloc, + special: Some(SpecialAllocatorMethod::Realloc), inputs: &[ AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr }, AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }, @@ -69,6 +85,7 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ }, AllocatorMethod { name: sym::alloc_zeroed, + special: Some(SpecialAllocatorMethod::AllocZeroed), inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }], output: AllocatorTy::ResultPtr, }, diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index a7e83f65151ae..de0b85ebb63b8 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -1,13 +1,13 @@ use libc::c_uint; use rustc_ast::expand::allocator::{ - AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name, + AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, SpecialAllocatorMethod, + default_fn_name, global_fn_name, }; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, OomStrategy}; -use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; use crate::attributes::llfn_attrs_from_instance; @@ -65,12 +65,12 @@ pub(crate) unsafe fn codegen( 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 alloc_attr_flag = match method.special { + Some(SpecialAllocatorMethod::Alloc) => CodegenFnAttrFlags::ALLOCATOR, + Some(SpecialAllocatorMethod::Dealloc) => CodegenFnAttrFlags::DEALLOCATOR, + Some(SpecialAllocatorMethod::Realloc) => CodegenFnAttrFlags::REALLOCATOR, + Some(SpecialAllocatorMethod::AllocZeroed) => CodegenFnAttrFlags::ALLOCATOR_ZEROED, + None => CodegenFnAttrFlags::empty(), }; let mut attrs = CodegenFnAttrs::new(); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 1a79038d1fcd1..ecb1750ddfd34 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -669,6 +669,7 @@ pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec { pub(crate) pthread_rwlock_sanity: Cell, pub(crate) pthread_condvar_sanity: Cell, + /// (Foreign) symbols that are synthesized as part of the allocator shim: the key indicates the + /// name of the symbol being synthesized; the value indicates whether this should invoke some + /// other symbol or whether this has special allocator semantics. + pub(crate) allocator_shim_symbols: FxHashMap>, /// Cache for `mangle_internal_symbol`. pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>, @@ -819,6 +826,7 @@ impl<'tcx> MiriMachine<'tcx> { pthread_mutex_sanity: Cell::new(false), pthread_rwlock_sanity: Cell::new(false), pthread_condvar_sanity: Cell::new(false), + allocator_shim_symbols: Self::allocator_shim_symbols(tcx), mangle_internal_symbol_cache: Default::default(), force_intrinsic_fallback: config.force_intrinsic_fallback, float_nondet: config.float_nondet, @@ -827,6 +835,36 @@ impl<'tcx> MiriMachine<'tcx> { } } + fn allocator_shim_symbols( + tcx: TyCtxt<'tcx>, + ) -> FxHashMap> { + use rustc_codegen_ssa::base::allocator_shim_contents; + + // codegen uses `allocator_kind_for_codegen` here, but that's only needed to deal with + // dylibs which we do not support. + let Some(kind) = tcx.allocator_kind(()) else { + return Default::default(); + }; + let methods = allocator_shim_contents(tcx, kind); + let mut symbols = FxHashMap::default(); + for method in methods { + let from_name = Symbol::intern(&mangle_internal_symbol( + tcx, + &allocator::global_fn_name(method.name), + )); + let to = match method.special { + Some(special) => Either::Right(special), + None => + Either::Left(Symbol::intern(&mangle_internal_symbol( + tcx, + &allocator::default_fn_name(method.name), + ))), + }; + symbols.try_insert(from_name, to).unwrap(); + } + symbols + } + pub(crate) fn late_init( ecx: &mut MiriInterpCx<'tcx>, config: &MiriConfig, @@ -992,6 +1030,7 @@ impl VisitProvenance for MiriMachine<'_> { pthread_mutex_sanity: _, pthread_rwlock_sanity: _, pthread_condvar_sanity: _, + allocator_shim_symbols: _, mangle_internal_symbol_cache: _, force_intrinsic_fallback: _, float_nondet: _, diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index f05c5fbbe1d4f..f498a21c9f902 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -1,5 +1,8 @@ -use rustc_abi::{Align, Size}; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size}; +use rustc_ast::expand::allocator::SpecialAllocatorMethod; +use rustc_middle::ty::Ty; +use rustc_span::Symbol; +use rustc_target::callconv::FnAbi; use crate::*; @@ -54,30 +57,100 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Align::from_bytes(prev_power_of_two(size)).unwrap() } - /// Emulates calling the internal __rust_* allocator functions - fn emulate_allocator( + /// Check some basic requirements for this allocation request: + /// non-zero size, power-of-two alignment. + fn check_rust_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> { + let this = self.eval_context_ref(); + if size == 0 { + throw_ub_format!("creating allocation with size 0"); + } + if size > this.max_size_of_val().bytes() { + throw_ub_format!("creating an allocation larger than half the address space"); + } + if let Err(e) = Align::from_bytes(align) { + match e { + AlignFromBytesError::TooLarge(_) => { + throw_unsup_format!( + "creating allocation with alignment {align} exceeding rustc's maximum \ + supported value" + ); + } + AlignFromBytesError::NotPowerOfTwo(_) => { + throw_ub_format!("creating allocation with non-power-of-two alignment {align}"); + } + } + } + + interp_ok(()) + } + + fn rust_special_allocator_method( &mut self, - default: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + method: SpecialAllocatorMethod, + link_name: Symbol, + abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &[OpTy<'tcx>], + dest: &PlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let Some(allocator_kind) = this.tcx.allocator_kind(()) else { - // in real code, this symbol does not exist without an allocator - return interp_ok(EmulateItemResult::NotSupported); - }; + match method { + SpecialAllocatorMethod::Alloc | SpecialAllocatorMethod::AllocZeroed => { + let [size, align] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let size = this.read_target_usize(size)?; + let align = this.read_target_usize(align)?; + + this.check_rust_alloc_request(size, align)?; - match allocator_kind { - AllocatorKind::Global => { - // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion - // of this attribute. As such we have to call an exported Rust function, - // and not execute any Miri shim. Somewhat unintuitively doing so is done - // by returning `NotSupported`, which triggers the `lookup_exported_symbol` - // fallback case in `emulate_foreign_item`. - interp_ok(EmulateItemResult::NotSupported) + let ptr = this.allocate_ptr( + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::Rust.into(), + if matches!(method, SpecialAllocatorMethod::AllocZeroed) { + AllocInit::Zero + } else { + AllocInit::Uninit + }, + )?; + + this.write_pointer(ptr, dest) + } + SpecialAllocatorMethod::Dealloc => { + let [ptr, old_size, align] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let old_size = this.read_target_usize(old_size)?; + let align = this.read_target_usize(align)?; + + // No need to check old_size/align; we anyway check that they match the allocation. + this.deallocate_ptr( + ptr, + Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())), + MiriMemoryKind::Rust.into(), + ) } - AllocatorKind::Default => { - default(this)?; - interp_ok(EmulateItemResult::NeedsReturn) + SpecialAllocatorMethod::Realloc => { + let [ptr, old_size, align, new_size] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let old_size = this.read_target_usize(old_size)?; + let align = this.read_target_usize(align)?; + let new_size = this.read_target_usize(new_size)?; + // No need to check old_size; we anyway check that they match the allocation. + + this.check_rust_alloc_request(new_size, align)?; + + let align = Align::from_bytes(align).unwrap(); + let new_ptr = this.reallocate_ptr( + ptr, + Some((Size::from_bytes(old_size), align)), + Size::from_bytes(new_size), + align, + MiriMemoryKind::Rust.into(), + AllocInit::Uninit, + )?; + this.write_pointer(new_ptr, dest) } } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 26692e519a0bd..74818cf0740bd 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -2,8 +2,9 @@ use std::collections::hash_map::Entry; use std::io::Write; use std::path::Path; -use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size}; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_abi::{Align, CanonAbi, Size}; +use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE; +use rustc_data_structures::either::Either; use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; use rustc_hir::def_id::CrateNum; @@ -11,6 +12,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::AllocInit; use rustc_middle::ty::{Instance, Ty}; use rustc_middle::{mir, ty}; +use rustc_session::config::OomStrategy; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; @@ -50,31 +52,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { let this = self.eval_context_mut(); - // 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. - let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else { - // in real code, this symbol does not exist without an allocator - throw_unsup_format!( - "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set" - ); - }; - if handler_kind == AllocatorKind::Default { - let name = - Symbol::intern(this.mangle_internal_symbol("__rdl_alloc_error_handler")); + // Handle allocator shim. + if let Some(shim) = this.machine.allocator_shim_symbols.get(&link_name) { + match *shim { + Either::Left(other_fn) => { let handler = this - .lookup_exported_symbol(name)? + .lookup_exported_symbol(other_fn)? .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`. + Either::Right(special) => { + this.rust_special_allocator_method(special, link_name, abi, args, dest)?; + this.return_to_block(ret)?; + return interp_ok(None); + } } - _ => {} } // FIXME: avoid allocating memory @@ -254,33 +246,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { - /// Check some basic requirements for this allocation request: - /// non-zero size, power-of-two alignment. - fn check_rustc_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> { - let this = self.eval_context_ref(); - if size == 0 { - throw_ub_format!("creating allocation with size 0"); - } - if size > this.max_size_of_val().bytes() { - throw_ub_format!("creating an allocation larger than half the address space"); - } - if let Err(e) = Align::from_bytes(align) { - match e { - AlignFromBytesError::TooLarge(_) => { - throw_unsup_format!( - "creating allocation with alignment {align} exceeding rustc's maximum \ - supported value" - ); - } - AlignFromBytesError::NotPowerOfTwo(_) => { - throw_ub_format!("creating allocation with non-power-of-two alignment {align}"); - } - } - } - - interp_ok(()) - } - fn emulate_foreign_item_inner( &mut self, link_name: Symbol, @@ -340,7 +305,51 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Here we dispatch all the shims for foreign functions. If you have a platform specific // shim, add it to the corresponding submodule. match link_name.as_str() { + // Magic functions Rust emits (and not as part of the allocator shim). + name if name == this.mangle_internal_symbol(NO_ALLOC_SHIM_IS_UNSTABLE) => { + // This is a no-op shim that only exists to prevent making the allocator shims + // instantly stable. + let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + } + name if name == this.mangle_internal_symbol(OomStrategy::SYMBOL) => { + // Gets the value of the `oom` option. + let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let val = this.tcx.sess.opts.unstable_opts.oom.should_panic(); + this.write_int(val, dest)?; + } + // Miri-specific extern functions + "miri_alloc" => { + let [size, align] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let size = this.read_target_usize(size)?; + let align = this.read_target_usize(align)?; + + this.check_rust_alloc_request(size, align)?; + + let ptr = this.allocate_ptr( + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::Miri.into(), + AllocInit::Uninit, + )?; + + this.write_pointer(ptr, dest)?; + } + "miri_dealloc" => { + let [ptr, old_size, align] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let old_size = this.read_target_usize(old_size)?; + let align = this.read_target_usize(align)?; + + // No need to check old_size/align; we anyway check that they match the allocation. + this.deallocate_ptr( + ptr, + Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())), + MiriMemoryKind::Miri.into(), + )?; + } "miri_start_unwind" => { let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; @@ -492,7 +501,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } } - // GenMC mode: Assume statements block the current thread when their condition is false. "miri_genmc_assume" => { let [condition] = @@ -579,133 +587,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - // Rust allocation - name if name == this.mangle_internal_symbol("__rust_alloc") || name == "miri_alloc" => { - let default = |ecx: &mut MiriInterpCx<'tcx>| { - // Only call `check_shim` when `#[global_allocator]` isn't used. When that - // macro is used, we act like no shim exists, so that the exported function can run. - let [size, align] = - ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; - let size = ecx.read_target_usize(size)?; - let align = ecx.read_target_usize(align)?; - - ecx.check_rustc_alloc_request(size, align)?; - - let memory_kind = match link_name.as_str() { - "miri_alloc" => MiriMemoryKind::Miri, - _ => MiriMemoryKind::Rust, - }; - - let ptr = ecx.allocate_ptr( - Size::from_bytes(size), - Align::from_bytes(align).unwrap(), - memory_kind.into(), - AllocInit::Uninit, - )?; - - ecx.write_pointer(ptr, dest) - }; - - match link_name.as_str() { - "miri_alloc" => { - default(this)?; - return interp_ok(EmulateItemResult::NeedsReturn); - } - _ => return this.emulate_allocator(default), - } - } - name if name == this.mangle_internal_symbol("__rust_alloc_zeroed") => { - return this.emulate_allocator(|this| { - // See the comment for `__rust_alloc` why `check_shim` is only called in the - // default case. - let [size, align] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; - let size = this.read_target_usize(size)?; - let align = this.read_target_usize(align)?; - - this.check_rustc_alloc_request(size, align)?; - - let ptr = this.allocate_ptr( - Size::from_bytes(size), - Align::from_bytes(align).unwrap(), - MiriMemoryKind::Rust.into(), - AllocInit::Zero, - )?; - this.write_pointer(ptr, dest) - }); - } - name if name == this.mangle_internal_symbol("__rust_dealloc") - || name == "miri_dealloc" => - { - let default = |ecx: &mut MiriInterpCx<'tcx>| { - // See the comment for `__rust_alloc` why `check_shim` is only called in the - // default case. - let [ptr, old_size, align] = - ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; - let ptr = ecx.read_pointer(ptr)?; - let old_size = ecx.read_target_usize(old_size)?; - let align = ecx.read_target_usize(align)?; - - let memory_kind = match link_name.as_str() { - "miri_dealloc" => MiriMemoryKind::Miri, - _ => MiriMemoryKind::Rust, - }; - - // No need to check old_size/align; we anyway check that they match the allocation. - ecx.deallocate_ptr( - ptr, - Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())), - memory_kind.into(), - ) - }; - - match link_name.as_str() { - "miri_dealloc" => { - default(this)?; - return interp_ok(EmulateItemResult::NeedsReturn); - } - _ => return this.emulate_allocator(default), - } - } - name if name == this.mangle_internal_symbol("__rust_realloc") => { - return this.emulate_allocator(|this| { - // See the comment for `__rust_alloc` why `check_shim` is only called in the - // default case. - let [ptr, old_size, align, new_size] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; - let ptr = this.read_pointer(ptr)?; - let old_size = this.read_target_usize(old_size)?; - let align = this.read_target_usize(align)?; - let new_size = this.read_target_usize(new_size)?; - // No need to check old_size; we anyway check that they match the allocation. - - this.check_rustc_alloc_request(new_size, align)?; - - let align = Align::from_bytes(align).unwrap(); - let new_ptr = this.reallocate_ptr( - ptr, - Some((Size::from_bytes(old_size), align)), - Size::from_bytes(new_size), - align, - MiriMemoryKind::Rust.into(), - AllocInit::Uninit, - )?; - this.write_pointer(new_ptr, dest) - }); - } - name if name == this.mangle_internal_symbol("__rust_no_alloc_shim_is_unstable_v2") => { - // This is a no-op shim that only exists to prevent making the allocator shims instantly stable. - let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; - } - name if name - == this.mangle_internal_symbol("__rust_alloc_error_handler_should_panic_v2") => - { - // Gets the value of the `oom` option. - let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; - let val = this.tcx.sess.opts.unstable_opts.oom.should_panic(); - this.write_int(val, dest)?; - } - // C memory handling functions "memcmp" => { let [left, right, n] = From 87e0f43c563b9efc02f62a0e9c30acea0d83f7dd Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Wed, 15 Oct 2025 21:15:41 +0100 Subject: [PATCH 11/11] Revert unintentional whitespace changes to rustfmt-excluded file --- src/tools/miri/src/bin/log/tracing_chrome.rs | 56 +++++++-------- src/tools/miri/src/intrinsics/simd.rs | 74 ++++++++++---------- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/tools/miri/src/bin/log/tracing_chrome.rs b/src/tools/miri/src/bin/log/tracing_chrome.rs index f4bff76c2029d..85b4de62a5ec4 100644 --- a/src/tools/miri/src/bin/log/tracing_chrome.rs +++ b/src/tools/miri/src/bin/log/tracing_chrome.rs @@ -523,16 +523,16 @@ where } }, TraceStyle::Async => Some( - span.scope() - .from_root() - .take(1) - .next() - .unwrap_or(span) - .id() - .into_u64() + span.scope() + .from_root() + .take(1) + .next() + .unwrap_or(span) + .id() + .into_u64() .cast_signed() // the comment above explains the cast ), - } + } } fn enter_span(&self, span: SpanRef, ts: f64, tid: usize, out: &Sender) { @@ -567,11 +567,11 @@ where Some(thread_data) => (thread_data, false), None => { let tid = self.max_tid.fetch_add(1, Ordering::SeqCst); - let out = self.out.lock().unwrap().clone(); + let out = self.out.lock().unwrap().clone(); let start = TracingChromeInstant::setup_for_thread_and_start(tid); *thread_data = Some(ThreadData { tid, out, start }); (thread_data.as_mut().unwrap(), true) - } + } }; start.with_elapsed_micros_subtracting_tracing(|ts| { @@ -583,7 +583,7 @@ where let _ignored = out.send(Message::NewThread(*tid, name)); } f(ts, *tid, out); - }); + }); }); } } @@ -605,15 +605,15 @@ where fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { if self.include_args { self.with_elapsed_micros_subtracting_tracing(|_, _, _| { - let span = ctx.span(id).unwrap(); - let mut exts = span.extensions_mut(); + let span = ctx.span(id).unwrap(); + let mut exts = span.extensions_mut(); - let args = exts.get_mut::(); + let args = exts.get_mut::(); - if let Some(args) = args { - let args = Arc::make_mut(&mut args.args); - values.record(&mut JsonVisitor { object: args }); - } + if let Some(args) = args { + let args = Arc::make_mut(&mut args.args); + values.record(&mut JsonVisitor { object: args }); + } }); } } @@ -636,16 +636,16 @@ where fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { - if self.include_args { - let mut args = Object::new(); - attrs.record(&mut JsonVisitor { object: &mut args }); - ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper { - args: Arc::new(args), - }); - } - if let TraceStyle::Threaded = self.trace_style { - return; - } + if self.include_args { + let mut args = Object::new(); + attrs.record(&mut JsonVisitor { object: &mut args }); + ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper { + args: Arc::new(args), + }); + } + if let TraceStyle::Threaded = self.trace_style { + return; + } self.enter_span(ctx.span(id).expect("Span not found."), ts, tid, out); }); diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index bf7834422a812..1e7366b5a8269 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -38,47 +38,47 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { for i in 0..dest_len { let op = this.read_immediate(&this.project_index(&op, i)?)?; let dest = this.project_index(&dest, i)?; - let ty::Float(float_ty) = op.layout.ty.kind() else { - span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) - }; - // Using host floats except for sqrt (but it's fine, these operations do not - // have guaranteed precision). + let ty::Float(float_ty) = op.layout.ty.kind() else { + span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) + }; + // Using host floats except for sqrt (but it's fine, these operations do not + // have guaranteed precision). let val = match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => { - let f = op.to_scalar().to_f32()?; + FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F32 => { + let f = op.to_scalar().to_f32()?; let res = match intrinsic_name { - "fsqrt" => math::sqrt(f), - "fsin" => f.to_host().sin().to_soft(), - "fcos" => f.to_host().cos().to_soft(), - "fexp" => f.to_host().exp().to_soft(), - "fexp2" => f.to_host().exp2().to_soft(), - "flog" => f.to_host().ln().to_soft(), - "flog2" => f.to_host().log2().to_soft(), - "flog10" => f.to_host().log10().to_soft(), - _ => bug!(), - }; - let res = this.adjust_nan(res, &[f]); - Scalar::from(res) - } - FloatTy::F64 => { - let f = op.to_scalar().to_f64()?; + "fsqrt" => math::sqrt(f), + "fsin" => f.to_host().sin().to_soft(), + "fcos" => f.to_host().cos().to_soft(), + "fexp" => f.to_host().exp().to_soft(), + "fexp2" => f.to_host().exp2().to_soft(), + "flog" => f.to_host().ln().to_soft(), + "flog2" => f.to_host().log2().to_soft(), + "flog10" => f.to_host().log10().to_soft(), + _ => bug!(), + }; + let res = this.adjust_nan(res, &[f]); + Scalar::from(res) + } + FloatTy::F64 => { + let f = op.to_scalar().to_f64()?; let res = match intrinsic_name { - "fsqrt" => math::sqrt(f), - "fsin" => f.to_host().sin().to_soft(), - "fcos" => f.to_host().cos().to_soft(), - "fexp" => f.to_host().exp().to_soft(), - "fexp2" => f.to_host().exp2().to_soft(), - "flog" => f.to_host().ln().to_soft(), - "flog2" => f.to_host().log2().to_soft(), - "flog10" => f.to_host().log10().to_soft(), - _ => bug!(), - }; - let res = this.adjust_nan(res, &[f]); - Scalar::from(res) - } - FloatTy::F128 => unimplemented!("f16_f128"), + "fsqrt" => math::sqrt(f), + "fsin" => f.to_host().sin().to_soft(), + "fcos" => f.to_host().cos().to_soft(), + "fexp" => f.to_host().exp().to_soft(), + "fexp2" => f.to_host().exp2().to_soft(), + "flog" => f.to_host().ln().to_soft(), + "flog2" => f.to_host().log2().to_soft(), + "flog10" => f.to_host().log10().to_soft(), + _ => bug!(), }; + let res = this.adjust_nan(res, &[f]); + Scalar::from(res) + } + FloatTy::F128 => unimplemented!("f16_f128"), + }; this.write_scalar(val, &dest)?; }