From 7f3569b1741e9fec563abe7d6a3ca80af282d3e8 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:09:50 +0200 Subject: [PATCH 01/22] Implement diagnostic::opaque attribute --- .../src/attributes/diagnostic/mod.rs | 1 + .../src/attributes/diagnostic/opaque.rs | 64 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 2 + .../rustc_attr_parsing/src/diagnostics.rs | 4 ++ compiler/rustc_errors/src/emitter.rs | 14 ++-- compiler/rustc_expand/src/base.rs | 16 +++-- compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 + .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_resolve/src/macros.rs | 1 + compiler/rustc_span/src/hygiene.rs | 5 ++ compiler/rustc_span/src/lib.rs | 31 +++++---- compiler/rustc_span/src/symbol.rs | 2 + .../opaque/auxiliary/wrap.rs | 9 +++ .../diagnostic_namespace/opaque/duplicate.rs | 9 +++ .../opaque/duplicate.stderr | 19 ++++++ ...highlight_maccall.-Zmacro-backtrace.stderr | 16 +++++ .../opaque/highlight_maccall.default.stderr | 8 +++ .../opaque/highlight_maccall.rs | 17 +++++ ...ly_point_at_input.-Zmacro-backtrace.stderr | 18 ++++++ .../opaque/only_point_at_input.default.stderr | 15 +++++ .../opaque/only_point_at_input.rs | 23 +++++++ .../feature-gate-diagnostic-opaque.rs | 13 ++++ .../feature-gate-diagnostic-opaque.stderr | 23 +++++++ .../ui/include-macros/mismatched-types.stderr | 9 +-- 27 files changed, 296 insertions(+), 31 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs create mode 100644 tests/ui/diagnostic_namespace/opaque/auxiliary/wrap.rs create mode 100644 tests/ui/diagnostic_namespace/opaque/duplicate.rs create mode 100644 tests/ui/diagnostic_namespace/opaque/duplicate.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/highlight_maccall.-Zmacro-backtrace.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/highlight_maccall.default.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/highlight_maccall.rs create mode 100644 tests/ui/diagnostic_namespace/opaque/only_point_at_input.-Zmacro-backtrace.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/only_point_at_input.default.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/only_point_at_input.rs create mode 100644 tests/ui/feature-gates/feature-gate-diagnostic-opaque.rs create mode 100644 tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 435367e7dcfbe..2486b5e3a65ed 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -28,6 +28,7 @@ pub(crate) mod on_type_error; pub(crate) mod on_unimplemented; pub(crate) mod on_unknown; pub(crate) mod on_unmatched_args; +pub(crate) mod opaque; #[derive(Copy, Clone)] pub(crate) enum Mode { diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs new file mode 100644 index 0000000000000..0a18b69dda0ed --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs @@ -0,0 +1,64 @@ +use rustc_feature::AttributeStability; +use rustc_hir::Target; +use rustc_hir::attrs::AttributeKind; +use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; +use rustc_span::{Span, sym}; + +use crate::attributes::{AcceptMapping, AttributeParser}; +use crate::context::{AcceptContext, FinalizeContext}; +use crate::diagnostics::OpaqueDoesNotExpectArgs; +use crate::parser::ArgParser; +use crate::target_checking::AllowedTargets; +use crate::target_checking::Policy::Allow; +use crate::{template, unstable}; + +#[derive(Default)] +pub(crate) struct OpaqueParser { + attr_span: Option, +} + +impl AttributeParser for OpaqueParser { + const ATTRIBUTES: AcceptMapping = &[ + ( + &[sym::diagnostic, sym::opaque], + template!(Word), + AttributeStability::Stable, // Unstable, stability checked manually in the parser + |this, cx, args| { + if !cx.features().diagnostic_opaque() { + return; + } + this.parse(cx, args); + }, + ), + ( + // For use on exported macros, where using tool attributes is an error. + &[sym::rustc_diagnostic_opaque], + template!(Word), + unstable!( + rustc_attrs, + "see `#[diagnostic::opaque]` for the nightly equivalent of this attribute" + ), + OpaqueParser::parse, + ), + ]; + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowListWarnRest(&[Allow(Target::MacroDef)]); + + fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + if let Some(_) = self.attr_span { Some(AttributeKind::Opaque) } else { None } + } +} + +impl OpaqueParser { + fn parse<'sess>(&mut self, cx: &mut AcceptContext<'_, 'sess>, args: &ArgParser) { + let attr_span = cx.attr_span; + if let Some(earlier_span) = self.attr_span { + cx.warn_unused_duplicate(earlier_span, attr_span); + } + self.attr_span = Some(attr_span); + + if !matches!(args, ArgParser::NoArgs) { + cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES, OpaqueDoesNotExpectArgs, attr_span); + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1595d2f80ddc7..a14a8d4381a0b 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -36,6 +36,7 @@ use crate::attributes::diagnostic::on_type_error::*; use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::diagnostic::on_unknown::*; use crate::attributes::diagnostic::on_unmatched_args::*; +use crate::attributes::diagnostic::opaque::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; use crate::attributes::inline::*; @@ -151,6 +152,7 @@ attribute_parsers!( OnUnimplementedParser, OnUnknownParser, OnUnmatchedArgsParser, + OpaqueParser, RustcAlignParser, RustcAlignStaticParser, RustcCguTestAttributeParser, diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index 50667952b814d..e360f5221c138 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -279,6 +279,10 @@ pub(crate) struct AttrCrateLevelOnly; #[diag("`#[diagnostic::do_not_recommend]` does not expect any arguments")] pub(crate) struct DoNotRecommendDoesNotExpectArgs; +#[derive(Diagnostic)] +#[diag("`#[diagnostic::opaque]` does not expect any arguments")] +pub(crate) struct OpaqueDoesNotExpectArgs; + #[derive(Diagnostic)] #[diag("invalid `crate_type` value")] pub(crate) struct UnknownCrateTypes { diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index fa3ff21b2726f..d6651f7801e11 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -188,8 +188,7 @@ pub trait Emitter { self.render_multispans_macro_backtrace(span, children, backtrace); if !backtrace { - // Skip builtin macros, as their expansion isn't relevant to the end user. This includes - // actual intrinsics, like `asm!`. + // Skip macros annotated with `#[diagnostic::opaque]`. Builtin macros are "opaque" too. if let Some((macro_kind, name, _)) = has_macro_spans.first() && let Some((_, _, false)) = has_macro_spans.last() { @@ -334,6 +333,13 @@ pub trait Emitter { // we move these spans from the external macros to their corresponding use site. fn fix_multispan_in_extern_macros(&self, span: &mut MultiSpan) { let Some(source_map) = self.source_map() else { return }; + let should_hide = |span| { + source_map.is_imported(span) || { + let expn = span.data().ctxt.outer_expn_data(); + expn.diagnostic_opaque && matches!(expn.kind, ExpnKind::Macro(MacroKind::Bang, _)) + } + }; + // First, find all the spans in external macros and point instead at their use site. let replacements: Vec<(Span, Span)> = span .primary_spans() @@ -341,11 +347,11 @@ pub trait Emitter { .copied() .chain(span.span_labels().iter().map(|sp_label| sp_label.span)) .filter_map(|sp| { - if !sp.is_dummy() && source_map.is_imported(sp) { + if !sp.is_dummy() && should_hide(sp) { let mut span = sp; while let Some(callsite) = span.parent_callsite() { span = callsite; - if !source_map.is_imported(span) { + if !should_hide(span) { return Some((sp, span)); } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index ee516991cfbfc..b829ff7eb1abd 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -807,6 +807,8 @@ pub struct SyntaxExtension { /// Suppresses the "this error originates in the macro" note when a diagnostic points at this /// macro. pub hide_backtrace: bool, + /// Prevents diagnostics pointing into this macro. + pub diagnostic_opaque: bool, } impl SyntaxExtension { @@ -841,6 +843,7 @@ impl SyntaxExtension { local_inner_macros: false, collapse_debuginfo: false, hide_backtrace: false, + diagnostic_opaque: false, } } @@ -870,12 +873,6 @@ impl SyntaxExtension { collapse_table[flag as usize][attr as usize] } - fn get_hide_backtrace(attrs: &[hir::Attribute]) -> bool { - // FIXME(estebank): instead of reusing `#[rustc_diagnostic_item]` as a proxy, introduce a - // new attribute purely for this under the `#[diagnostic]` namespace. - find_attr!(attrs, RustcDiagnosticItem(..)) - } - /// Constructs a syntax extension with the given properties /// and other properties converted from attributes. pub fn new( @@ -910,7 +907,10 @@ impl SyntaxExtension { // Not a built-in macro None => (None, helper_attrs), }; - let hide_backtrace = builtin_name.is_some() || Self::get_hide_backtrace(attrs); + let diagnostic_opaque = builtin_name.is_some() + || (!sess.opts.unstable_opts.macro_backtrace + && find_attr!(attrs, Opaque | RustcDiagnosticItem(..))); + let hide_backtrace = diagnostic_opaque; let stability = find_attr!(attrs, Stability { stability, .. } => *stability); @@ -939,6 +939,7 @@ impl SyntaxExtension { local_inner_macros, collapse_debuginfo, hide_backtrace, + diagnostic_opaque, } } @@ -1025,6 +1026,7 @@ impl SyntaxExtension { self.local_inner_macros, self.collapse_debuginfo, self.hide_backtrace, + self.diagnostic_opaque, ) } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e081c2989d3c6..be6979314b53a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -320,6 +320,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ // Used by the `rustc::bad_opt_access` lint on fields // types (as well as any others in future). sym::rustc_lint_opt_deny_field_access, + sym::rustc_diagnostic_opaque, // ========================================================================== // Internal attributes, Const related: diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index fc38d9de6e143..4c5e999117248 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -518,6 +518,8 @@ declare_features! ( (unstable, diagnostic_on_unknown, "1.96.0", Some(152900)), /// Allows macros to customize macro argument matcher diagnostics. (unstable, diagnostic_on_unmatched_args, "1.97.0", Some(155642)), + // Used by macros to not show their bodies in error messages. No-op with `-Z macro-backtrace`. + (unstable, diagnostic_opaque, "CURRENT_RUSTC_VERSION", Some(158813)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 17d00863d99d5..3e8c11f0166c1 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1263,6 +1263,9 @@ pub enum AttributeKind { directive: Option>, }, + /// Represents `#[diagnostic::opaque]`. + Opaque, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index f5a6eeec07406..100979b8159d3 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -83,6 +83,7 @@ impl AttributeKind { OnUnimplemented { .. } => Yes, OnUnknown { .. } => Yes, OnUnmatchedArgs { .. } => Yes, + Opaque => Yes, Optimize(..) => No, PanicRuntime => No, PatchableFunctionEntry { .. } => Yes, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7ae6005e9bc98..90339d5bac686 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -296,6 +296,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::NoStd { .. } => (), AttributeKind::OnUnknown { .. } => (), AttributeKind::OnUnmatchedArgs { .. } => (), + AttributeKind::Opaque => (), AttributeKind::Optimize(..) => (), AttributeKind::PanicRuntime => (), AttributeKind::PatchableFunctionEntry { .. } => (), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 595c2fdf011dd..171b2cfc0d9ab 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -721,6 +721,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (sym::on_unknown, Some(sym::diagnostic_on_unknown)), (sym::on_unmatched_args, Some(sym::diagnostic_on_unmatched_args)), (sym::on_type_error, Some(sym::diagnostic_on_type_error)), + (sym::opaque, Some(sym::diagnostic_opaque)), ]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 1c742052783cd..3c3fe9389245d 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1022,6 +1022,8 @@ pub struct ExpnData { pub(crate) collapse_debuginfo: bool, /// When true, we do not display the note telling people to use the `-Zmacro-backtrace` flag. pub hide_backtrace: bool, + /// Prevents diagnostics pointing into this, if it is a macro expansion. + pub diagnostic_opaque: bool, } impl !PartialEq for ExpnData {} @@ -1041,6 +1043,7 @@ impl ExpnData { local_inner_macros: bool, collapse_debuginfo: bool, hide_backtrace: bool, + diagnostic_opaque: bool, ) -> ExpnData { ExpnData { kind, @@ -1056,6 +1059,7 @@ impl ExpnData { local_inner_macros, collapse_debuginfo, hide_backtrace, + diagnostic_opaque, } } @@ -1081,6 +1085,7 @@ impl ExpnData { local_inner_macros: false, collapse_debuginfo: false, hide_backtrace: false, + diagnostic_opaque: false, } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 5015741f10c5f..80d40c2f4c5b5 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1238,20 +1238,25 @@ impl Span { /// If "self" is the span of the outer_ident, and "within" is the span of the `($ident,)` /// expr, then this will return the span of the `$ident` macro variable. pub fn within_macro(self, within: Span, sm: &SourceMap) -> Option { - match Span::prepare_to_combine(self, within) { - // Only return something if it doesn't overlap with the original span, - // and the span isn't "imported" (i.e. from unavailable sources). - // FIXME: This does limit the usefulness of the error when the macro is - // from a foreign crate; we could also take into account `-Zmacro-backtrace`, - // which doesn't redact this span (but that would mean passing in even more - // args to this function, lol). - Ok((self_, _, parent)) - if self_.hi < self.lo() || self.hi() < self_.lo && !sm.is_imported(within) => - { - Some(Span::new(self_.lo, self_.hi, self_.ctxt, parent)) - } - _ => None, + let (self_, _, parent) = Span::prepare_to_combine(self, within).ok()?; + + // Only return something if it doesn't overlap with the original span + // and the span isn't "imported" (i.e. from unavailable sources). + // FIXME: This does limit the usefulness of the error when the macro is + // from a foreign crate; we could also take into account `-Zmacro-backtrace`, + // which doesn't redact this span (but that would mean passing in even more + // args to this function, lol). + if self.data().contains(self_) || sm.is_imported(within) { + return None; } + + // Don't return something if it's marked with `#[diagnostic::opaque]`. + // This already accounts for `-Zmacro-backtrace`. + if within.data().ctxt.outer_expn_data().diagnostic_opaque { + return None; + } + + Some(Span::new(self_.lo, self_.hi, self_.ctxt, parent)) } pub fn from_inner(self, inner: InnerSpan) -> Span { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 71eae246ebaff..6ba3112f3e7e0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -819,6 +819,7 @@ symbols! { diagnostic_on_unknown, diagnostic_on_unmatch_args, diagnostic_on_unmatched_args, + diagnostic_opaque, dialect, direct, discriminant_kind, @@ -1775,6 +1776,7 @@ symbols! { rustc_deprecated_safe_2024, rustc_diagnostic_item, rustc_diagnostic_macros, + rustc_diagnostic_opaque, rustc_do_not_const_check, rustc_doc_primitive, rustc_driver, diff --git a/tests/ui/diagnostic_namespace/opaque/auxiliary/wrap.rs b/tests/ui/diagnostic_namespace/opaque/auxiliary/wrap.rs new file mode 100644 index 0000000000000..f046e82b8f9e7 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/auxiliary/wrap.rs @@ -0,0 +1,9 @@ +#![feature(diagnostic_opaque)] + +#[diagnostic::opaque] +#[macro_export] +macro_rules! wrap { + ($x:ident) => {{ + let x = blah::$x; + }}; +} diff --git a/tests/ui/diagnostic_namespace/opaque/duplicate.rs b/tests/ui/diagnostic_namespace/opaque/duplicate.rs new file mode 100644 index 0000000000000..2341e72ddc5e3 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/duplicate.rs @@ -0,0 +1,9 @@ +#![crate_type = "lib"] +#![feature(diagnostic_opaque)] +#![deny(unused_attributes)] +#[diagnostic::opaque] +#[diagnostic::opaque] +//~^ERROR unused attribute +macro_rules! m { + () => {} +} diff --git a/tests/ui/diagnostic_namespace/opaque/duplicate.stderr b/tests/ui/diagnostic_namespace/opaque/duplicate.stderr new file mode 100644 index 0000000000000..d4420d3b63a9f --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/duplicate.stderr @@ -0,0 +1,19 @@ +error: unused attribute + --> $DIR/duplicate.rs:5:1 + | +LL | #[diagnostic::opaque] + | ^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/duplicate.rs:4:1 + | +LL | #[diagnostic::opaque] + | ^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/duplicate.rs:3:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/opaque/highlight_maccall.-Zmacro-backtrace.stderr b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.-Zmacro-backtrace.stderr new file mode 100644 index 0000000000000..d53e5cb526061 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.-Zmacro-backtrace.stderr @@ -0,0 +1,16 @@ +error: oh no + --> $DIR/highlight_maccall.rs:9:9 + | +LL | / macro_rules! my_error { +LL | | () => {{ +LL | | compile_error!("oh no") + | | ^^^^^^^^^^^^^^^^^^^^^^^ +... | +LL | | } + | |_- in this expansion of `my_error!` +... +LL | my_error!(); + | ----------- in this macro invocation + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/opaque/highlight_maccall.default.stderr b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.default.stderr new file mode 100644 index 0000000000000..07521e003e3e2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.default.stderr @@ -0,0 +1,8 @@ +error: oh no + --> $DIR/highlight_maccall.rs:16:5 + | +LL | my_error!(); + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/opaque/highlight_maccall.rs b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.rs new file mode 100644 index 0000000000000..f8095838b2368 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.rs @@ -0,0 +1,17 @@ +//@revisions: default -Zmacro-backtrace +//@[-Zmacro-backtrace] compile-flags: -Z macro-backtrace + +#![feature(diagnostic_opaque)] + +#[diagnostic::opaque] +macro_rules! my_error { + () => {{ + compile_error!("oh no") + //~^ ERROR oh no + }} +} + + +fn main() { + my_error!(); +} diff --git a/tests/ui/diagnostic_namespace/opaque/only_point_at_input.-Zmacro-backtrace.stderr b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.-Zmacro-backtrace.stderr new file mode 100644 index 0000000000000..47ed10e2bcfdc --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.-Zmacro-backtrace.stderr @@ -0,0 +1,18 @@ +error[E0425]: cannot find value `x` in module `blah` + --> $DIR/only_point_at_input.rs:17:17 + | +LL | wrap::wrap!(x); + | ^ not found in `blah` + +error[E0425]: cannot find value `x` in module `blah` + --> $DIR/only_point_at_input.rs:20:17 + | +LL | let x = blah::$x; + | -- due to this macro variable +... +LL | local_wrap!(x); + | ^ not found in `blah` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/diagnostic_namespace/opaque/only_point_at_input.default.stderr b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.default.stderr new file mode 100644 index 0000000000000..366e1c3aead24 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.default.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find value `x` in module `blah` + --> $DIR/only_point_at_input.rs:17:17 + | +LL | wrap::wrap!(x); + | ^ not found in `blah` + +error[E0425]: cannot find value `x` in module `blah` + --> $DIR/only_point_at_input.rs:20:17 + | +LL | local_wrap!(x); + | ^ not found in `blah` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/diagnostic_namespace/opaque/only_point_at_input.rs b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.rs new file mode 100644 index 0000000000000..003b6dbacf6c0 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.rs @@ -0,0 +1,23 @@ +//@revisions: default -Zmacro-backtrace +//@[-Zmacro-backtrace] compile-flags: -Z macro-backtrace +//@ aux-crate:wrap=wrap.rs +#![feature(diagnostic_opaque)] + +mod blah {} + +#[diagnostic::opaque] +macro_rules! local_wrap { + ($x:ident) => {{ + let x = blah::$x; + }}; +} + + +fn main() { + wrap::wrap!(x); + //~^ ERROR cannot find value `x` in module `blah` + + local_wrap!(x); + //~^ ERROR cannot find value `x` in module `blah` + +} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-opaque.rs b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.rs new file mode 100644 index 0000000000000..0dc3a4569098f --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.rs @@ -0,0 +1,13 @@ +#![crate_type = "lib"] +#![feature(decl_macro)] +#![deny(unknown_diagnostic_attributes)] + +#[diagnostic::opaque] +//~^ ERROR unknown diagnostic attribute +macro_rules! foo { + () => {} +} + +#[diagnostic::opaque] +//~^ ERROR unknown diagnostic attribute +macro bar() {} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr new file mode 100644 index 0000000000000..90426a1324e81 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr @@ -0,0 +1,23 @@ +error: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-opaque.rs:5:15 + | +LL | #[diagnostic::opaque] + | ^^^^^^ + | + = help: add `#![feature(diagnostic_opaque)]` to the crate attributes to enable +note: the lint level is defined here + --> $DIR/feature-gate-diagnostic-opaque.rs:3:9 + | +LL | #![deny(unknown_diagnostic_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-opaque.rs:11:15 + | +LL | #[diagnostic::opaque] + | ^^^^^^ + | + = help: add `#![feature(diagnostic_opaque)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + diff --git a/tests/ui/include-macros/mismatched-types.stderr b/tests/ui/include-macros/mismatched-types.stderr index 4fab832c2d8e8..ab6b599c2beda 100644 --- a/tests/ui/include-macros/mismatched-types.stderr +++ b/tests/ui/include-macros/mismatched-types.stderr @@ -1,13 +1,8 @@ error[E0308]: mismatched types - --> $DIR/file.txt:0:1 - | -LL | - | ^ expected `&[u8]`, found `&str` - | - ::: $DIR/mismatched-types.rs:2:12 + --> $DIR/mismatched-types.rs:2:20 | LL | let b: &[u8] = include_str!("file.txt"); - | ----- ------------------------ in this macro invocation + | ----- ^^^^^^^^^^^^^^^^^^^^^^^^ expected `&[u8]`, found `&str` | | | expected due to this | From 6a17b7948e0c47fc46f01b3d282f515e436b9a4f Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:31:38 +0200 Subject: [PATCH 02/22] Remove `hide_backtrace` --- compiler/rustc_errors/src/emitter.rs | 2 +- compiler/rustc_expand/src/base.rs | 10 ++-------- compiler/rustc_span/src/hygiene.rs | 8 ++------ 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index d6651f7801e11..6d5f8462ff496 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -175,7 +175,7 @@ pub trait Emitter { ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None, ExpnKind::Macro(macro_kind, name) => { - Some((macro_kind, name, expn_data.hide_backtrace)) + Some((macro_kind, name, expn_data.diagnostic_opaque)) } } }) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index b829ff7eb1abd..08142f40aeff3 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -804,10 +804,8 @@ pub struct SyntaxExtension { /// Should debuginfo for the macro be collapsed to the outermost expansion site (in other /// words, was the macro definition annotated with `#[collapse_debuginfo]`)? pub collapse_debuginfo: bool, - /// Suppresses the "this error originates in the macro" note when a diagnostic points at this - /// macro. - pub hide_backtrace: bool, - /// Prevents diagnostics pointing into this macro. + /// Prevents diagnostics pointing into this macro and suppresses the "this error originates in + /// the macro" note when a diagnostic points at this macro. pub diagnostic_opaque: bool, } @@ -842,7 +840,6 @@ impl SyntaxExtension { allow_internal_unsafe: false, local_inner_macros: false, collapse_debuginfo: false, - hide_backtrace: false, diagnostic_opaque: false, } } @@ -910,7 +907,6 @@ impl SyntaxExtension { let diagnostic_opaque = builtin_name.is_some() || (!sess.opts.unstable_opts.macro_backtrace && find_attr!(attrs, Opaque | RustcDiagnosticItem(..))); - let hide_backtrace = diagnostic_opaque; let stability = find_attr!(attrs, Stability { stability, .. } => *stability); @@ -938,7 +934,6 @@ impl SyntaxExtension { allow_internal_unsafe, local_inner_macros, collapse_debuginfo, - hide_backtrace, diagnostic_opaque, } } @@ -1025,7 +1020,6 @@ impl SyntaxExtension { self.allow_internal_unsafe, self.local_inner_macros, self.collapse_debuginfo, - self.hide_backtrace, self.diagnostic_opaque, ) } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 3c3fe9389245d..1cdce5cd04567 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1020,9 +1020,8 @@ pub struct ExpnData { /// Should debuginfo for the macro be collapsed to the outermost expansion site (in other /// words, was the macro definition annotated with `#[collapse_debuginfo]`)? pub(crate) collapse_debuginfo: bool, - /// When true, we do not display the note telling people to use the `-Zmacro-backtrace` flag. - pub hide_backtrace: bool, - /// Prevents diagnostics pointing into this, if it is a macro expansion. + /// When true, we prevent diagnostics pointing into this macro, if it is one, and we do not + /// display the note telling people to use the `-Zmacro-backtrace` flag. pub diagnostic_opaque: bool, } @@ -1042,7 +1041,6 @@ impl ExpnData { allow_internal_unsafe: bool, local_inner_macros: bool, collapse_debuginfo: bool, - hide_backtrace: bool, diagnostic_opaque: bool, ) -> ExpnData { ExpnData { @@ -1058,7 +1056,6 @@ impl ExpnData { allow_internal_unsafe, local_inner_macros, collapse_debuginfo, - hide_backtrace, diagnostic_opaque, } } @@ -1084,7 +1081,6 @@ impl ExpnData { allow_internal_unsafe: false, local_inner_macros: false, collapse_debuginfo: false, - hide_backtrace: false, diagnostic_opaque: false, } } From 106bb96346de3b9c334b11c58a6588d09d153abf Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:33:52 +0200 Subject: [PATCH 03/22] Remove `rustc_diagnostic_item` hack --- compiler/rustc_expand/src/base.rs | 3 +-- library/alloc/src/macros.rs | 2 ++ library/core/src/lib.rs | 1 + library/core/src/macros/mod.rs | 8 ++++++++ library/core/src/pin.rs | 1 + library/std/src/macros.rs | 3 +++ library/std/src/thread/local.rs | 1 + 7 files changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 08142f40aeff3..323299c74d6fb 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -905,8 +905,7 @@ impl SyntaxExtension { None => (None, helper_attrs), }; let diagnostic_opaque = builtin_name.is_some() - || (!sess.opts.unstable_opts.macro_backtrace - && find_attr!(attrs, Opaque | RustcDiagnosticItem(..))); + || (!sess.opts.unstable_opts.macro_backtrace && find_attr!(attrs, Opaque)); let stability = find_attr!(attrs, Stability { stability, .. } => *stability); diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index b99107fb345a4..d93e279df7cfb 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -39,6 +39,7 @@ #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "vec_macro"] #[allow_internal_unstable(rustc_attrs, liballoc_internals)] +#[rustc_diagnostic_opaque] macro_rules! vec { () => ( $crate::vec::Vec::new() @@ -108,6 +109,7 @@ macro_rules! vec { #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(hint_must_use, liballoc_internals)] #[rustc_diagnostic_item = "format_macro"] +#[rustc_diagnostic_opaque] macro_rules! format { ($($arg:tt)*) => { $crate::__export::must_use({ diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 8e310cc7d2155..7aab486903a06 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -121,6 +121,7 @@ #![feature(derive_const)] #![feature(diagnostic_on_const)] #![feature(diagnostic_on_unmatched_args)] +#![feature(diagnostic_opaque)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] #![feature(extern_types)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 932a2fc0bad92..4b1ce0351f7d7 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -39,6 +39,7 @@ macro_rules! panic { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "assert_eq_macro"] #[allow_internal_unstable(panic_internals)] +#[rustc_diagnostic_opaque] macro_rules! assert_eq { ($left:expr, $right:expr $(,)?) => {{ match (&$left, &$right) { @@ -95,6 +96,7 @@ macro_rules! assert_eq { #[stable(feature = "assert_ne", since = "1.13.0")] #[rustc_diagnostic_item = "assert_ne_macro"] #[allow_internal_unstable(panic_internals)] +#[rustc_diagnostic_opaque] macro_rules! assert_ne { ($left:expr, $right:expr $(,)?) => {{ match (&$left, &$right) { @@ -284,6 +286,7 @@ pub macro cfg_select($($tt:tt)*) { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "debug_assert_macro"] #[allow_internal_unstable(edition_panic)] +#[rustc_diagnostic_opaque] macro_rules! debug_assert { ($($arg:tt)*) => { if $crate::cfg!(debug_assertions) { @@ -424,6 +427,7 @@ pub macro debug_assert_matches($($arg:tt)*) { #[stable(feature = "matches_macro", since = "1.42.0")] #[rustc_diagnostic_item = "matches_macro"] #[allow_internal_unstable(non_exhaustive_omitted_patterns_lint, stmt_expr_attributes)] +#[rustc_diagnostic_opaque] macro_rules! matches { ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { #[allow(non_exhaustive_omitted_patterns)] @@ -600,6 +604,7 @@ macro_rules! r#try { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "write_macro"] +#[rustc_diagnostic_opaque] macro_rules! write { ($dst:expr, $($arg:tt)*) => { $dst.write_fmt($crate::format_args!($($arg)*)) @@ -638,6 +643,7 @@ macro_rules! write { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "writeln_macro"] #[allow_internal_unstable(format_args_nl)] +#[rustc_diagnostic_opaque] macro_rules! writeln { ($dst:expr $(,)?) => { $crate::write!($dst, "\n") @@ -793,6 +799,7 @@ macro_rules! unreachable { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "unimplemented_macro"] #[allow_internal_unstable(panic_internals)] +#[rustc_diagnostic_opaque] macro_rules! unimplemented { () => { $crate::panicking::panic("not implemented") @@ -873,6 +880,7 @@ macro_rules! unimplemented { #[stable(feature = "todo_macro", since = "1.40.0")] #[rustc_diagnostic_item = "todo_macro"] #[allow_internal_unstable(panic_internals)] +#[rustc_diagnostic_opaque] macro_rules! todo { () => { $crate::panicking::panic("not yet implemented") diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 40f774806046a..b766a767df64d 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -2025,6 +2025,7 @@ unsafe impl PinCoerceUnsized for Pin {} #[rustc_diagnostic_item = "pin_macro"] // `super` gets removed by rustfmt #[rustfmt::skip] +#[diagnostic::opaque] pub macro pin($value:expr $(,)?) { 'p: { super let mut pinned = $value; diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 8bcf870e9aeb4..64415809b4a33 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -82,6 +82,7 @@ macro_rules! panic { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "print_macro")] #[allow_internal_unstable(print_internals)] +#[rustc_diagnostic_opaque] macro_rules! print { ($($arg:tt)*) => {{ $crate::io::_print($crate::format_args!($($arg)*)); @@ -138,6 +139,7 @@ macro_rules! print { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "println_macro")] #[allow_internal_unstable(print_internals, format_args_nl)] +#[rustc_diagnostic_opaque] macro_rules! println { () => { $crate::print!("\n") @@ -352,6 +354,7 @@ macro_rules! eprintln { #[macro_export] #[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")] #[stable(feature = "dbg_macro", since = "1.32.0")] +#[rustc_diagnostic_opaque] macro_rules! dbg { // NOTE: We cannot use `concat!` to make a static string as a format argument // of `eprintln!` because `file!` could contain a `{` or diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index ec0ba9970e479..9365e54765d4c 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -346,6 +346,7 @@ pub macro thread_local_process_attrs { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")] #[allow_internal_unstable(thread_local_internals)] +#[rustc_diagnostic_opaque] macro_rules! thread_local { () => {}; From ec8c18fdb83bbda486ad0e175dfc407727076341 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sat, 2 May 2026 19:35:54 -0700 Subject: [PATCH 04/22] Un-copy-paste Dropper in VecDeque code --- .../alloc/src/collections/vec_deque/mod.rs | 48 +++++-------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 44211354f520e..80b69b1dba557 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -133,21 +133,21 @@ impl Clone for VecDeque { } } -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T, A: Allocator> Drop for VecDeque { - fn drop(&mut self) { - /// Runs the destructor for all items in the slice when it gets dropped (normally or - /// during unwinding). - struct Dropper<'a, T>(&'a mut [T]); +/// Runs the destructor for all items in the slice when it gets dropped (normally or +/// during unwinding). +struct Dropper<'a, T>(&'a mut [T]); - impl<'a, T> Drop for Dropper<'a, T> { - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(self.0); - } - } +impl Drop for Dropper<'_, T> { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.0); } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T, A: Allocator> Drop for VecDeque { + fn drop(&mut self) { let (front, back) = self.as_mut_slices(); unsafe { let _back_dropper = Dropper(back); @@ -1433,18 +1433,6 @@ impl VecDeque { #[doc(alias = "retain_front")] #[stable(feature = "deque_extras", since = "1.16.0")] pub fn truncate(&mut self, len: usize) { - /// Runs the destructor for all items in the slice when it gets dropped (normally or - /// during unwinding). - struct Dropper<'a, T>(&'a mut [T]); - - impl<'a, T> Drop for Dropper<'a, T> { - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(self.0); - } - } - } - // Safe because: // // * Any slice passed to `drop_in_place` is valid; the second case has @@ -1499,18 +1487,6 @@ impl VecDeque { #[doc(alias = "truncate_front")] #[stable(feature = "vec_deque_truncate_front", since = "CURRENT_RUSTC_VERSION")] pub fn retain_back(&mut self, len: usize) { - /// Runs the destructor for all items in the slice when it gets dropped (normally or - /// during unwinding). - struct Dropper<'a, T>(&'a mut [T]); - - impl<'a, T> Drop for Dropper<'a, T> { - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(self.0); - } - } - } - unsafe { if len >= self.len { // No action is taken From ba313ddd1db0c8264be2f20473f561ce1942fe53 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sat, 2 May 2026 19:36:08 -0700 Subject: [PATCH 05/22] Implement `VecDeque::retain_range` --- .../alloc/src/collections/vec_deque/mod.rs | 86 +++++++++++ library/alloctests/tests/lib.rs | 1 + library/alloctests/tests/vec_deque.rs | 135 ++++++++++++++++++ 3 files changed, 222 insertions(+) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 80b69b1dba557..9eac8bcde1d1c 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1519,6 +1519,92 @@ impl VecDeque { } } + /// Shortens the deque to the elements within `range`, dropping the rest. + /// + /// # Panics + /// + /// Panics if the starting point is greater than the end point or if + /// the end point is greater than the length of the deque. + /// + /// # Examples + /// + /// ``` + /// # #![feature(vec_deque_retain_range)] + /// use std::collections::VecDeque; + /// + /// let mut buf: VecDeque<_> = (0..6).collect(); + /// buf.truncate_to_range(2..5); + /// assert_eq!(buf, [2, 3, 4]); + /// ``` + #[unstable(feature = "vec_deque_retain_range", issue = "156215")] + pub fn truncate_to_range(&mut self, range: R) + where + R: RangeBounds, + { + let Range { start, end } = slice::range(range, ..self.len); + + if start == 0 && end == self.len { + return; + } else if start == end { + self.clear(); + return; + } else if start == 0 { + self.truncate(end); + return; + } else if end == self.len { + self.retain_back(self.len - start); + return; + } + + // Both the dropped prefix [0..start) and the dropped suffix [end..self.len) are + // non-empty. Plan up to three physical slices to drop, then update head/len, then + // drop. Only one of the dropped prefix or dropped suffix can cross between slices. + let (front, back) = self.as_mut_slices(); + let flen = front.len(); + let blen = back.len(); + let fptr = front.as_mut_ptr(); + let bptr = back.as_mut_ptr(); + + unsafe { + let (drop_a, drop_b, drop_c) = if end <= flen { + // Kept range lies in `front`. The dropped suffix is the rest of `front` + // plus all of `back`. + let pre = ptr::slice_from_raw_parts_mut(fptr, start); + let mid = ptr::slice_from_raw_parts_mut(fptr.add(end), flen - end); + (pre, mid, Some(back as *mut [T])) + } else if start >= flen { + // Kept range lies in `back`. The dropped prefix is all of `front` plus the + // start of `back`. + let mid = ptr::slice_from_raw_parts_mut(bptr, start - flen); + let suf = ptr::slice_from_raw_parts_mut(bptr.add(end - flen), blen - (end - flen)); + (front as *mut [T], mid, Some(suf)) + } else { + // Kept range straddles the boundary. The dropped prefix is in `front`, the + // dropped suffix is in `back`. Only two regions to drop. + let pre = ptr::slice_from_raw_parts_mut(fptr, start); + let suf = ptr::slice_from_raw_parts_mut(bptr.add(end - flen), blen - (end - flen)); + (pre, suf, None) + }; + + // Set these once only, then drop. If we called truncate + retain_back, a panic in + // a destructor could leave this truncation in a half completed state. + self.head = self.to_wrapped_index(start); + self.len = end - start; + + match drop_c { + Some(c) => { + let _g_a = Dropper(&mut *drop_a); + let _g_b = Dropper(&mut *drop_b); + ptr::drop_in_place(c); + } + None => { + let _g_a = Dropper(&mut *drop_a); + ptr::drop_in_place(drop_b); + } + } + } + } + /// Returns a reference to the underlying allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index e5b3646f14a58..c06a22cbfb5d8 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -40,6 +40,7 @@ #![feature(vec_peek_mut)] #![feature(vec_try_remove)] #![feature(ptr_cast_slice)] +#![feature(vec_deque_retain_range)] #![allow(internal_features)] #![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index 2e75b7b07e63f..15cc156d6988f 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -7,6 +7,7 @@ use std::collections::vec_deque::Drain; use std::fmt::Debug; use std::ops::Bound::*; use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::sync::atomic::{AtomicUsize, Ordering}; use Taggy::*; use Taggypar::*; @@ -2360,3 +2361,137 @@ fn test_splice_wrapping_and_resize() { assert_eq!(Vec::from(vec), [1, 2, 3, 4, 1, 1, 1, 1, 1]) } + +#[test] +fn truncate_to_range_basic() { + // no-op + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(..); + assert_eq!(v, [0, 1, 2, 3, 4, 5]); + + // clear + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(3..3); + assert_eq!(v, [] as [i32; 0]); + + // truncate + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(..3); + assert_eq!(v, [0, 1, 2]); + + // truncate front + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(2..); + assert_eq!(v, [2, 3, 4, 5]); + + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(2..5); + assert_eq!(v, [2, 3, 4]); + + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(3..=5); + assert_eq!(v, [3, 4, 5]); + + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(..=3); + assert_eq!(v, [0, 1, 2, 3]); +} + +fn make_wrapped() -> VecDeque { + let mut v = VecDeque::new(); + v.extend(0..5); + v.push_front(-1); + v.push_front(-2); + v.push_front(-3); + assert_eq!(v.as_slices(), ([-3, -2, -1].as_slice(), [0, 1, 2, 3, 4].as_slice())); + v +} + +#[test] +fn truncate_to_range_kept_in_front() { + let mut v = make_wrapped(); + v.truncate_to_range(1..3); + assert_eq!(v, [-2, -1]); +} + +#[test] +fn truncate_to_range_kept_in_back() { + let mut v = make_wrapped(); + v.truncate_to_range(4..7); + assert_eq!(v, [1, 2, 3]); +} + +#[test] +fn truncate_to_range_kept_straddles() { + let mut v = make_wrapped(); + v.truncate_to_range(1..6); + assert_eq!(v, [-2, -1, 0, 1, 2]); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn truncate_to_range_leak() { + struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } ); + + let mut q = VecDeque::new(); + q.push_back(D(true)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_front(D(false)); + q.push_front(D(false)); + q.push_front(D(false)); + + catch_unwind(AssertUnwindSafe(|| q.truncate_to_range(4..7))).ok(); + + assert_eq!(DROPS.get(), 5); +} + +#[test] +fn truncate_to_range_calls_drop() { + static DROPPED: AtomicUsize = AtomicUsize::new(0); + + #[derive(Debug)] + struct Foo(u8); + + impl Drop for Foo { + fn drop(&mut self) { + DROPPED.fetch_add(1, Ordering::Relaxed); + } + } + + let mut deque: VecDeque<_> = (0..12).map(Foo).collect(); + deque.truncate_to_range(1..5); + assert!(deque.iter().map(|x| x.0).eq([1, 2, 3, 4])); + assert_eq!(8, DROPPED.load(Ordering::Relaxed)); +} + +#[test] +#[should_panic] +fn truncate_to_range_start_greater_than_end() { + let mut v: VecDeque<_> = (0..6).collect(); + #[allow(clippy::reversed_empty_ranges)] + v.truncate_to_range(4..2); +} + +#[test] +#[should_panic] +fn truncate_to_range_end_past_len() { + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(2..7); +} + +#[test] +#[should_panic] +fn truncate_to_range_start_past_len() { + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(7..8); +} + +#[test] +#[should_panic] +fn truncate_to_range_inclusive_end_overflow() { + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(0..=usize::MAX); +} From f371a62bdf3bdf868dc29a5d9b19140ded6d35f7 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Wed, 13 May 2026 07:42:32 -0700 Subject: [PATCH 06/22] Tidy up library/alloctests/tests/lib.rs --- library/alloctests/tests/lib.rs | 58 +++++++++++++++++---------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index c06a22cbfb5d8..96dcde71fc071 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -1,49 +1,51 @@ +// tidy-alphabetical-start +#![allow(internal_features)] +#![deny(implicit_provenance_casts)] +#![deny(unsafe_op_in_unsafe_fn)] #![feature(allocator_api)] +#![feature(binary_heap_drain_sorted)] +#![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_pop_if)] -#![feature(const_heap)] -#![feature(deque_extend_front)] -#![feature(iter_array_chunks)] #![feature(casefold)] -#![feature(cow_is_borrowed)] +#![feature(const_btree_len)] +#![feature(const_heap)] +#![feature(const_trait_impl)] #![feature(core_intrinsics)] +#![feature(cow_is_borrowed)] +#![feature(deque_extend_front)] #![feature(downcast_unchecked)] +#![feature(drain_keep_rest)] #![feature(exact_size_is_empty)] #![feature(hashmap_internals)] +#![feature(inplace_iteration)] +#![feature(iter_advance_by)] +#![feature(iter_array_chunks)] +#![feature(iter_next_chunk)] #![feature(linked_list_cursors)] +#![feature(local_waker)] +#![feature(macro_metavar_expr_concat)] #![feature(map_try_insert)] #![feature(pattern)] -#![feature(trusted_len)] -#![feature(try_reserve_kind)] -#![feature(try_with_capacity)] -#![feature(unboxed_closures)] -#![feature(binary_heap_into_iter_sorted)] -#![feature(binary_heap_drain_sorted)] -#![feature(slice_ptr_get)] -#![feature(slice_range)] +#![feature(ptr_cast_slice)] #![feature(slice_partial_sort_unstable)] -#![feature(inplace_iteration)] -#![feature(iter_advance_by)] -#![feature(iter_next_chunk)] #![feature(slice_partition_dedup)] -#![feature(string_remove_matches)] -#![feature(const_btree_len)] -#![feature(const_trait_impl)] -#![feature(test)] -#![feature(thin_box)] -#![feature(drain_keep_rest)] -#![feature(local_waker)] +#![feature(slice_ptr_get)] +#![feature(slice_range)] #![feature(str_as_str)] #![feature(strict_provenance_lints)] +#![feature(string_remove_matches)] #![feature(string_replace_in_place)] +#![feature(test)] +#![feature(thin_box)] +#![feature(trusted_len)] +#![feature(try_reserve_kind)] +#![feature(try_with_capacity)] +#![feature(unboxed_closures)] #![feature(unique_rc_arc)] -#![feature(macro_metavar_expr_concat)] +#![feature(vec_deque_retain_range)] #![feature(vec_peek_mut)] #![feature(vec_try_remove)] -#![feature(ptr_cast_slice)] -#![feature(vec_deque_retain_range)] -#![allow(internal_features)] -#![deny(implicit_provenance_casts)] -#![deny(unsafe_op_in_unsafe_fn)] +// tidy-alphabetical-end extern crate alloc; From a98d8ad23691acaf3b337b45976d53a692822c90 Mon Sep 17 00:00:00 2001 From: cclfmht Date: Sun, 12 Jul 2026 08:47:09 +0800 Subject: [PATCH 07/22] Fix static-mut-refs lint check logic Previously, the lint might suggested using interior mutable type even if it the compiler already decides that the referenced type is interior mutable, while it didn't give such suggestion when the referenced type is considered non-interior mutable. This commit refined the logic as follow: if the referenced type is not interior mutable, then suggests using types with interior mutability; otherwise, suggests removing `mut` if the reference is a *shared* reference. Note that in the latter case, compiler might be silent if the span of the `static mut` definition is not appropriate for suggestions (e.g., comes from macro expansion). --- compiler/rustc_lint/src/lints.rs | 2 +- compiler/rustc_lint/src/static_mut_refs.rs | 9 +++++++-- .../borrowck-unsafe-static-mutable-borrows.stderr | 1 + tests/ui/consts/const_let_assign2.stderr | 1 + ...tatic-mut-refs-interior-mutability-no-sugg.stderr | 1 - .../lint/static-mut-refs-interior-mutability.stderr | 2 +- tests/ui/lint/static-mut-refs.e2021.stderr | 12 ++++++++++++ tests/ui/lint/static-mut-refs.e2024.stderr | 12 ++++++++++++ ...thread-local-static-mut-borrow-outlives-fn.stderr | 1 + tests/ui/statics/issue-15261.stderr | 1 + .../statics/static-lazy-init-with-arena-set.stderr | 2 +- tests/ui/statics/static-mut-shared-parens.stderr | 2 ++ tests/ui/statics/static-mut-xc.stderr | 7 +++++++ tests/ui/statics/static-recursive.stderr | 2 ++ 14 files changed, 49 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index eb82afab13186..d2bb70b07f8c8 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2728,7 +2728,7 @@ pub(crate) enum MutRefSugg { #[derive(Subdiagnostic)] #[suggestion( - "this type already provides \"interior mutability\", so its binding doesn't need to be declared as mutable", + "this type already provides \"interior mutability\", so its binding doesn't need to be declared as mutable when borrowed with a shared reference", style = "verbose", applicability = "maybe-incorrect", code = "" diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs index 67e83d0652c3d..78dfcf41a8309 100644 --- a/compiler/rustc_lint/src/static_mut_refs.rs +++ b/compiler/rustc_lint/src/static_mut_refs.rs @@ -184,7 +184,7 @@ fn emit_static_mut_refs( }; let (interior_mutability_help, interior_mutability_sugg) = - interior_mutability_suggestion(cx, def_id); + interior_mutability_suggestion(cx, def_id, mut_note); cx.emit_span_lint( STATIC_MUT_REFS, @@ -208,17 +208,22 @@ fn emit_static_mut_refs( fn interior_mutability_suggestion( cx: &LateContext<'_>, def_id: DefId, + mut_ref: bool, ) -> (bool, Option) { let static_ty = cx.tcx.type_of(def_id).skip_binder(); let has_interior_mutability = !static_ty.is_freeze(cx.tcx, cx.typing_env()); if !has_interior_mutability { + return (true, None); + } + + if mut_ref { return (false, None); } let sugg = static_mutability_span(cx, def_id).map(|span| StaticMutRefsInteriorMutabilitySugg { span }); - (sugg.is_none(), sugg) + (false, sugg) } fn static_mutability_span(cx: &LateContext<'_>, def_id: DefId) -> Option { diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr index 6c3fd9eb400f2..58ea9fe5a74b6 100644 --- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr @@ -5,6 +5,7 @@ LL | let sfoo: *mut Foo = &mut SFOO; | ^^^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw mut` instead to create a raw pointer diff --git a/tests/ui/consts/const_let_assign2.stderr b/tests/ui/consts/const_let_assign2.stderr index e4edb58461054..ef6202cf7df85 100644 --- a/tests/ui/consts/const_let_assign2.stderr +++ b/tests/ui/consts/const_let_assign2.stderr @@ -5,6 +5,7 @@ LL | let ptr = unsafe { &mut BB }; | ^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw mut` instead to create a raw pointer diff --git a/tests/ui/lint/static-mut-refs-interior-mutability-no-sugg.stderr b/tests/ui/lint/static-mut-refs-interior-mutability-no-sugg.stderr index 5ce69e1a14d83..e5db09a670cc9 100644 --- a/tests/ui/lint/static-mut-refs-interior-mutability-no-sugg.stderr +++ b/tests/ui/lint/static-mut-refs-interior-mutability-no-sugg.stderr @@ -5,7 +5,6 @@ LL | let _lock = unsafe { MACRO_MUTEX.lock().unwrap() }; | ^^^^^^^^^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default diff --git a/tests/ui/lint/static-mut-refs-interior-mutability.stderr b/tests/ui/lint/static-mut-refs-interior-mutability.stderr index 29ab5a5c404de..d6a644374d8a2 100644 --- a/tests/ui/lint/static-mut-refs-interior-mutability.stderr +++ b/tests/ui/lint/static-mut-refs-interior-mutability.stderr @@ -7,7 +7,7 @@ LL | let _lock = unsafe { STDINOUT_MUTEX.lock().unwrap() }; = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: for more information, see = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default -help: this type already provides "interior mutability", so its binding doesn't need to be declared as mutable +help: this type already provides "interior mutability", so its binding doesn't need to be declared as mutable when borrowed with a shared reference | LL - static mut STDINOUT_MUTEX: Mutex = Mutex::new(false); LL + static STDINOUT_MUTEX: Mutex = Mutex::new(false); diff --git a/tests/ui/lint/static-mut-refs.e2021.stderr b/tests/ui/lint/static-mut-refs.e2021.stderr index 56b4ad239afe3..89a4f3cc4932d 100644 --- a/tests/ui/lint/static-mut-refs.e2021.stderr +++ b/tests/ui/lint/static-mut-refs.e2021.stderr @@ -5,6 +5,7 @@ LL | let _y = &X; | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer @@ -19,6 +20,7 @@ LL | let _y = &mut X; | ^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw mut` instead to create a raw pointer | @@ -32,6 +34,7 @@ LL | let ref _a = X; | ^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see warning: creating a shared reference to mutable static @@ -41,6 +44,7 @@ LL | let (_b, _c) = (&X, &Y); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -54,6 +58,7 @@ LL | let (_b, _c) = (&X, &Y); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -67,6 +72,7 @@ LL | foo(&X); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -80,6 +86,7 @@ LL | let _ = Z.len(); | ^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see warning: creating a shared reference to mutable static @@ -89,6 +96,7 @@ LL | let _ = format!("{:?}", Z); | ^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see warning: creating a shared reference to mutable static @@ -98,6 +106,7 @@ LL | let _v = &A.value; | ^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -111,6 +120,7 @@ LL | let _s = &A.s.value; | ^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -124,6 +134,7 @@ LL | let ref _v = A.value; | ^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see warning: creating a mutable reference to mutable static @@ -136,6 +147,7 @@ LL | let _x = bar!(FOO); | --------- in this macro invocation | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: this warning originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/lint/static-mut-refs.e2024.stderr b/tests/ui/lint/static-mut-refs.e2024.stderr index 0b7f48a507c94..a0c4e9d9620a0 100644 --- a/tests/ui/lint/static-mut-refs.e2024.stderr +++ b/tests/ui/lint/static-mut-refs.e2024.stderr @@ -5,6 +5,7 @@ LL | let _y = &X; | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer @@ -19,6 +20,7 @@ LL | let _y = &mut X; | ^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw mut` instead to create a raw pointer | @@ -32,6 +34,7 @@ LL | let ref _a = X; | ^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see error: creating a shared reference to mutable static @@ -41,6 +44,7 @@ LL | let (_b, _c) = (&X, &Y); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -54,6 +58,7 @@ LL | let (_b, _c) = (&X, &Y); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -67,6 +72,7 @@ LL | foo(&X); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -80,6 +86,7 @@ LL | let _ = Z.len(); | ^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see error: creating a shared reference to mutable static @@ -89,6 +96,7 @@ LL | let _ = format!("{:?}", Z); | ^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see error: creating a shared reference to mutable static @@ -98,6 +106,7 @@ LL | let _v = &A.value; | ^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -111,6 +120,7 @@ LL | let _s = &A.s.value; | ^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -124,6 +134,7 @@ LL | let ref _v = A.value; | ^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see error: creating a mutable reference to mutable static @@ -136,6 +147,7 @@ LL | let _x = bar!(FOO); | --------- in this macro invocation | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr index 19a3a786ac940..95ca981679b65 100644 --- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr @@ -5,6 +5,7 @@ LL | S1 { a: unsafe { &mut X1 } } | ^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw mut` instead to create a raw pointer diff --git a/tests/ui/statics/issue-15261.stderr b/tests/ui/statics/issue-15261.stderr index fdc8e0c1f5ef7..b9578523de3a9 100644 --- a/tests/ui/statics/issue-15261.stderr +++ b/tests/ui/statics/issue-15261.stderr @@ -5,6 +5,7 @@ LL | static n: &'static usize = unsafe { &n_mut }; | ^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer diff --git a/tests/ui/statics/static-lazy-init-with-arena-set.stderr b/tests/ui/statics/static-lazy-init-with-arena-set.stderr index 43b244607885d..4aed8b06166c8 100644 --- a/tests/ui/statics/static-lazy-init-with-arena-set.stderr +++ b/tests/ui/statics/static-lazy-init-with-arena-set.stderr @@ -12,7 +12,7 @@ LL | | }); = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default -help: this type already provides "interior mutability", so its binding doesn't need to be declared as mutable +help: this type already provides "interior mutability", so its binding doesn't need to be declared as mutable when borrowed with a shared reference | LL - static mut ONCE: Once = Once::new(); LL + static ONCE: Once = Once::new(); diff --git a/tests/ui/statics/static-mut-shared-parens.stderr b/tests/ui/statics/static-mut-shared-parens.stderr index 1265bdc7cf0d3..70e43f208ac7b 100644 --- a/tests/ui/statics/static-mut-shared-parens.stderr +++ b/tests/ui/statics/static-mut-shared-parens.stderr @@ -5,6 +5,7 @@ LL | let _ = unsafe { (&TEST) as *const usize }; | ^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer @@ -19,6 +20,7 @@ LL | let _ = unsafe { (&mut TEST) as *const usize }; | ^^^^^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw mut` instead to create a raw pointer | diff --git a/tests/ui/statics/static-mut-xc.stderr b/tests/ui/statics/static-mut-xc.stderr index d0b30ce6f8514..91be2b50a11f5 100644 --- a/tests/ui/statics/static-mut-xc.stderr +++ b/tests/ui/statics/static-mut-xc.stderr @@ -5,6 +5,7 @@ LL | assert_eq!(static_mut_xc::a, 3); | ^^^^^^^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default @@ -15,6 +16,7 @@ LL | assert_eq!(static_mut_xc::a, 4); | ^^^^^^^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see warning: creating a shared reference to mutable static @@ -24,6 +26,7 @@ LL | assert_eq!(static_mut_xc::a, 5); | ^^^^^^^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see warning: creating a shared reference to mutable static @@ -33,6 +36,7 @@ LL | assert_eq!(static_mut_xc::a, 15); | ^^^^^^^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see warning: creating a shared reference to mutable static @@ -42,6 +46,7 @@ LL | assert_eq!(static_mut_xc::a, -3); | ^^^^^^^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see warning: creating a shared reference to mutable static @@ -51,6 +56,7 @@ LL | static_bound(&static_mut_xc::a); | ^^^^^^^^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -64,6 +70,7 @@ LL | static_bound_set(&mut static_mut_xc::a); | ^^^^^^^^^^^^^^^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw mut` instead to create a raw pointer | diff --git a/tests/ui/statics/static-recursive.stderr b/tests/ui/statics/static-recursive.stderr index 1ae96dfd5a832..56200e9f53d07 100644 --- a/tests/ui/statics/static-recursive.stderr +++ b/tests/ui/statics/static-recursive.stderr @@ -5,6 +5,7 @@ LL | static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer @@ -19,6 +20,7 @@ LL | assert_eq!(S, *(S as *const *const u8)); | ^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see warning: 2 warnings emitted From cb055bc6f78bd7de9667f1a6090a112ebf1c5fa9 Mon Sep 17 00:00:00 2001 From: cclfmht Date: Sun, 12 Jul 2026 19:01:01 +0800 Subject: [PATCH 08/22] Do not suggest interior mutable types for explicit borrows For cases like `&a` or `&mut a`, we only leave a subdiagnostic to suggest user to use raw borrow operators. Using raw pointers in such cases are also equally viable solution but it's not appropriate to suggest using interior mutable types at the same time. --- compiler/rustc_lint/src/static_mut_refs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs index 78dfcf41a8309..0dbed4ed1c0da 100644 --- a/compiler/rustc_lint/src/static_mut_refs.rs +++ b/compiler/rustc_lint/src/static_mut_refs.rs @@ -184,7 +184,7 @@ fn emit_static_mut_refs( }; let (interior_mutability_help, interior_mutability_sugg) = - interior_mutability_suggestion(cx, def_id, mut_note); + interior_mutability_suggestion(cx, def_id, mut_note, suggest_addr_of); cx.emit_span_lint( STATIC_MUT_REFS, @@ -209,12 +209,13 @@ fn interior_mutability_suggestion( cx: &LateContext<'_>, def_id: DefId, mut_ref: bool, + suggest_addr_of: bool, ) -> (bool, Option) { let static_ty = cx.tcx.type_of(def_id).skip_binder(); let has_interior_mutability = !static_ty.is_freeze(cx.tcx, cx.typing_env()); if !has_interior_mutability { - return (true, None); + return (!suggest_addr_of, None); } if mut_ref { From 1e65341d5504281bfa0cbede63dcae4ea025ab7b Mon Sep 17 00:00:00 2001 From: cclfmht Date: Sun, 12 Jul 2026 19:22:12 +0800 Subject: [PATCH 09/22] Bless UI tests covered by `static_mut_refs` lint --- .../borrowck/borrowck-unsafe-static-mutable-borrows.stderr | 1 - tests/ui/consts/const_let_assign2.stderr | 1 - tests/ui/lint/static-mut-refs.e2021.stderr | 7 ------- tests/ui/lint/static-mut-refs.e2024.stderr | 7 ------- ...rowck-thread-local-static-mut-borrow-outlives-fn.stderr | 1 - tests/ui/statics/issue-15261.stderr | 1 - tests/ui/statics/static-mut-shared-parens.stderr | 2 -- tests/ui/statics/static-mut-xc.stderr | 2 -- tests/ui/statics/static-recursive.stderr | 1 - 9 files changed, 23 deletions(-) diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr index 58ea9fe5a74b6..6c3fd9eb400f2 100644 --- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr @@ -5,7 +5,6 @@ LL | let sfoo: *mut Foo = &mut SFOO; | ^^^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw mut` instead to create a raw pointer diff --git a/tests/ui/consts/const_let_assign2.stderr b/tests/ui/consts/const_let_assign2.stderr index ef6202cf7df85..e4edb58461054 100644 --- a/tests/ui/consts/const_let_assign2.stderr +++ b/tests/ui/consts/const_let_assign2.stderr @@ -5,7 +5,6 @@ LL | let ptr = unsafe { &mut BB }; | ^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw mut` instead to create a raw pointer diff --git a/tests/ui/lint/static-mut-refs.e2021.stderr b/tests/ui/lint/static-mut-refs.e2021.stderr index 89a4f3cc4932d..e616ba0aa4b28 100644 --- a/tests/ui/lint/static-mut-refs.e2021.stderr +++ b/tests/ui/lint/static-mut-refs.e2021.stderr @@ -5,7 +5,6 @@ LL | let _y = &X; | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer @@ -20,7 +19,6 @@ LL | let _y = &mut X; | ^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw mut` instead to create a raw pointer | @@ -44,7 +42,6 @@ LL | let (_b, _c) = (&X, &Y); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -58,7 +55,6 @@ LL | let (_b, _c) = (&X, &Y); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -72,7 +68,6 @@ LL | foo(&X); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -106,7 +101,6 @@ LL | let _v = &A.value; | ^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -120,7 +114,6 @@ LL | let _s = &A.s.value; | ^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | diff --git a/tests/ui/lint/static-mut-refs.e2024.stderr b/tests/ui/lint/static-mut-refs.e2024.stderr index a0c4e9d9620a0..a8985fc8a1763 100644 --- a/tests/ui/lint/static-mut-refs.e2024.stderr +++ b/tests/ui/lint/static-mut-refs.e2024.stderr @@ -5,7 +5,6 @@ LL | let _y = &X; | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer @@ -20,7 +19,6 @@ LL | let _y = &mut X; | ^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw mut` instead to create a raw pointer | @@ -44,7 +42,6 @@ LL | let (_b, _c) = (&X, &Y); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -58,7 +55,6 @@ LL | let (_b, _c) = (&X, &Y); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -72,7 +68,6 @@ LL | foo(&X); | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -106,7 +101,6 @@ LL | let _v = &A.value; | ^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -120,7 +114,6 @@ LL | let _s = &A.s.value; | ^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr index 95ca981679b65..19a3a786ac940 100644 --- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr @@ -5,7 +5,6 @@ LL | S1 { a: unsafe { &mut X1 } } | ^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw mut` instead to create a raw pointer diff --git a/tests/ui/statics/issue-15261.stderr b/tests/ui/statics/issue-15261.stderr index b9578523de3a9..fdc8e0c1f5ef7 100644 --- a/tests/ui/statics/issue-15261.stderr +++ b/tests/ui/statics/issue-15261.stderr @@ -5,7 +5,6 @@ LL | static n: &'static usize = unsafe { &n_mut }; | ^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer diff --git a/tests/ui/statics/static-mut-shared-parens.stderr b/tests/ui/statics/static-mut-shared-parens.stderr index 70e43f208ac7b..1265bdc7cf0d3 100644 --- a/tests/ui/statics/static-mut-shared-parens.stderr +++ b/tests/ui/statics/static-mut-shared-parens.stderr @@ -5,7 +5,6 @@ LL | let _ = unsafe { (&TEST) as *const usize }; | ^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer @@ -20,7 +19,6 @@ LL | let _ = unsafe { (&mut TEST) as *const usize }; | ^^^^^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw mut` instead to create a raw pointer | diff --git a/tests/ui/statics/static-mut-xc.stderr b/tests/ui/statics/static-mut-xc.stderr index 91be2b50a11f5..f8fa25e152f67 100644 --- a/tests/ui/statics/static-mut-xc.stderr +++ b/tests/ui/statics/static-mut-xc.stderr @@ -56,7 +56,6 @@ LL | static_bound(&static_mut_xc::a); | ^^^^^^^^^^^^^^^^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw const` instead to create a raw pointer | @@ -70,7 +69,6 @@ LL | static_bound_set(&mut static_mut_xc::a); | ^^^^^^^^^^^^^^^^^^^^^ mutable reference to mutable static | = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see help: use `&raw mut` instead to create a raw pointer | diff --git a/tests/ui/statics/static-recursive.stderr b/tests/ui/statics/static-recursive.stderr index 56200e9f53d07..9578808ec7b03 100644 --- a/tests/ui/statics/static-recursive.stderr +++ b/tests/ui/statics/static-recursive.stderr @@ -5,7 +5,6 @@ LL | static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; | ^^ shared reference to mutable static | = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives - = help: use a type that relies on "interior mutability" instead; to read more on this, visit = note: for more information, see = note: `#[warn(static_mut_refs)]` (part of `#[warn(rust_2024_compatibility)]`) on by default help: use `&raw const` instead to create a raw pointer From b5e580d74e10c243f513dc28757ba4bdddd48ae5 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sun, 12 Jul 2026 15:23:24 -0400 Subject: [PATCH 10/22] Mark `PrivateItems` with `std_internals` unstable feature. --- library/core/src/marker/variance.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs index 31e30a16d45a3..9e135e89c451e 100644 --- a/library/core/src/marker/variance.rs +++ b/library/core/src/marker/variance.rs @@ -234,6 +234,7 @@ phantom_type! { } mod private_items { + #[unstable(feature = "std_internals", issue = "none")] pub trait PrivateItems { const VALUE: Self; } From 1592a0640401869e3440987a691258cd55f86a7a Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sun, 12 Jul 2026 17:20:31 -0300 Subject: [PATCH 11/22] Fix rustdoc auto trait normalization env --- src/librustdoc/clean/auto_trait.rs | 56 ++++++++++--------- src/librustdoc/core.rs | 10 +++- .../synthetic_auto/issue-159065.rs | 6 ++ 3 files changed, 44 insertions(+), 28 deletions(-) create mode 100644 tests/rustdoc-html/synthetic_auto/issue-159065.rs diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index f90cae4fb7e72..31ff38b78e70f 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -172,34 +172,36 @@ fn clean_param_env<'tcx>( // FIXME(#111101): Incorporate the explicit predicates of the item here... let item_clauses: FxIndexSet<_> = tcx.param_env(item_def_id).caller_bounds().iter().collect(); - let where_predicates = param_env - .caller_bounds() - .iter() - // FIXME: ...which hopefully allows us to simplify this: - .filter(|clause| { - !item_clauses.contains(clause) - || clause - .as_trait_clause() - .is_some_and(|clause| tcx.lang_items().sized_trait() == Some(clause.def_id())) - }) - .map(|clause| { - fold_regions(tcx, clause, |r, _| match r.kind() { - // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that - // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds - // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests. - // This is in dire need of an investigation into `AutoTraitFinder`. - ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r), - ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, - // FIXME(#120606): `AutoTraitFinder` can actually leak placeholder regions which feels - // incorrect. Needs investigation. - ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => { - bug!("unexpected region kind: {r:?}") - } + let where_predicates = cx.with_exact_param_env(param_env, |cx| { + param_env + .caller_bounds() + .iter() + // FIXME: ...which hopefully allows us to simplify this: + .filter(|clause| { + !item_clauses.contains(clause) + || clause.as_trait_clause().is_some_and(|clause| { + tcx.lang_items().sized_trait() == Some(clause.def_id()) + }) }) - }) - .flat_map(|clause| clean_predicate(clause, cx)) - .chain(clean_region_outlives_constraints(®ion_data, generics)) - .collect(); + .map(|clause| { + fold_regions(tcx, clause, |r, _| match r.kind() { + // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that + // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds + // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests. + // This is in dire need of an investigation into `AutoTraitFinder`. + ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r), + ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, + // FIXME(#120606): `AutoTraitFinder` can actually leak placeholder regions which feels + // incorrect. Needs investigation. + ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => { + bug!("unexpected region kind: {r:?}") + } + }) + }) + .flat_map(|clause| clean_predicate(clause, cx)) + .chain(clean_region_outlives_constraints(®ion_data, generics)) + .collect() + }); let mut generics = clean::Generics { params, where_predicates }; simplify::sizedness_bounds(cx, &mut generics); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 5bdedf7ae3d54..c1ae5f977cb89 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -87,7 +87,15 @@ impl<'tcx> DocContext<'tcx> { def_id: DefId, f: F, ) -> T { - let old_param_env = mem::replace(&mut self.param_env, self.tcx.param_env(def_id)); + self.with_exact_param_env(self.tcx.param_env(def_id), f) + } + + pub(crate) fn with_exact_param_env T>( + &mut self, + param_env: ParamEnv<'tcx>, + f: F, + ) -> T { + let old_param_env = mem::replace(&mut self.param_env, param_env); let ret = f(self); self.param_env = old_param_env; ret diff --git a/tests/rustdoc-html/synthetic_auto/issue-159065.rs b/tests/rustdoc-html/synthetic_auto/issue-159065.rs new file mode 100644 index 0000000000000..ff809c732cc35 --- /dev/null +++ b/tests/rustdoc-html/synthetic_auto/issue-159065.rs @@ -0,0 +1,6 @@ +//@ compile-flags: -Znext-solver=globally -Znormalize-docs + +#![crate_name = "foo"] + +#[doc(inline)] +pub use std as other; From 45b58f33a16959f751cabaf409cf0c4c0fb1a6b8 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Sun, 12 Jul 2026 14:15:59 -0700 Subject: [PATCH 12/22] OnceCell: Improve wording in module docs The "can also not" phrasing was slightly awkward, so rephrase this sentence. --- library/core/src/cell.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 9b2015ddc7101..1b8ec2a91478a 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -68,7 +68,7 @@ //! [`OnceCell`] is somewhat of a hybrid of `Cell` and `RefCell` that works for values that //! typically only need to be set once. This means that a reference `&T` can be obtained without //! moving or copying the inner value (unlike `Cell`) but also without runtime checks (unlike -//! `RefCell`). However, its value can also not be updated once set unless you have a mutable +//! `RefCell`). However, once set, its value cannot be updated unless you have a mutable //! reference to the `OnceCell`. //! //! `OnceCell` provides the following methods: From ab4b9bfc4783db90fd4cae6c09c2e2d2a72dc29c Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Wed, 24 Jun 2026 12:04:38 +0300 Subject: [PATCH 13/22] Add documentation for the `inline` attribute Document the built-in `inline` attribute in the standard library using the `#[doc(attribute = "...")]` mechanism, following the existing `must_use` attribute documentation. --- library/core/src/attribute_docs.rs | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/library/core/src/attribute_docs.rs b/library/core/src/attribute_docs.rs index 94ff7a8fee6d3..8591e866f482b 100644 --- a/library/core/src/attribute_docs.rs +++ b/library/core/src/attribute_docs.rs @@ -405,3 +405,43 @@ mod warn_attribute {} /// [`Result`]: result::Result /// [the `no_std` attribute]: ../reference/names/preludes.html#the-no_std-attribute mod no_std_attribute {} + +#[doc(attribute = "inline")] +// +/// Suggest that the compiler inline a function at its call sites. +/// +/// Inlining replaces a call with a copy of the called function's body, which can remove the +/// overhead of the call. The `inline` attribute is only a hint: the compiler may ignore it, and +/// it already inlines functions on its own when that looks worthwhile. Poor choices about what to +/// inline can make a program larger or slower. +/// +/// Where it does matter is inlining across crate boundaries. A non-generic function is not +/// normally inlined into another crate, since the calling crate compiles against only its +/// signature. Marking it `#[inline]` makes the body available to other crates so they can inline +/// it too: +/// +/// ```rust +/// # #![allow(dead_code)] +/// #[inline] +/// pub fn square(x: i32) -> i32 { +/// x * x +/// } +/// ``` +/// +/// Generic functions do not need this. They are instantiated in each crate that uses them, so +/// their bodies are already available to inline. +/// +/// The attribute applies to functions and has three forms: +/// +/// - `#[inline]` suggests inlining the function. +/// - `#[inline(always)]` suggests inlining it at every call site. +/// - `#[inline(never)]` suggests never inlining it. +/// +/// You should almost never need `#[inline(always)]`: prefer to let the compiler decide unless +/// profiling shows a small, hot function that benefits from it. `#[inline(never)]` is useful to +/// keep a rarely used path, such as a function that only reports an error, out of its caller. +/// +/// For more information, see the Reference on [the `inline` attribute]. +/// +/// [the `inline` attribute]: ../reference/attributes/codegen.html#the-inline-attribute +mod inline_attribute {} From 6866a94d49fbcf0de2baef4b21bdeabb88784ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jul 2026 22:58:56 +0000 Subject: [PATCH 14/22] Account for async closures when pointing at lifetime in return type ``` error: lifetime may not live long enough --> $DIR/higher-ranked-return.rs:11:46 | LL | let x = async move |x: &str| -> &str { | ________________________________-________-____^ | | | | | | | let's call the lifetime of this reference `'2` | | let's call the lifetime of this reference `'1` LL | | x LL | | }; | |_________^ returning this value requires that `'1` must outlive `'2` ``` instead of ``` error: lifetime may not live long enough --> $DIR/higher-ranked-return.rs:11:46 | LL | let x = async move |x: &str| -> &str { | ________________________________-________----_^ | | | | | | | return type of async closure `{async closure body@$DIR/higher-ranked-return.rs:11:46: 13:10}` contains a lifetime `'2` | | let's call the lifetime of this reference `'1` LL | | x LL | | }; | |_________^ returning this value requires that `'1` must outlive `'2` ``` --- compiler/rustc_borrowck/src/diagnostics/region_name.rs | 8 +++++++- .../async-closures/higher-ranked-return.stderr | 4 ++-- tests/ui/async-await/async-closures/not-lending.stderr | 4 ++-- .../issue-74072-lifetime-name-annotations.stderr | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index b8037fc83aad0..0e07de31c2baa 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -791,12 +791,18 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option { let tcx = self.infcx.tcx; - let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; + let mut return_ty = self.regioncx.universal_regions().unnormalized_output_ty; debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty); if !tcx.any_free_region_meets(&return_ty, |r| r.as_var() == fr) { return None; } + if let ty::Coroutine(_, args) = return_ty.kind() { + // When the return type is identified to be `{async closure body}`, we instead care + // about the actual return type of that coroutine. + return_ty = args.as_coroutine().return_ty(); + } + let mir_hir_id = self.mir_hir_id(); let (return_span, mir_description, hir_ty) = match tcx.hir_node(mir_hir_id) { diff --git a/tests/ui/async-await/async-closures/higher-ranked-return.stderr b/tests/ui/async-await/async-closures/higher-ranked-return.stderr index 23ce3df661654..efd5698e9731f 100644 --- a/tests/ui/async-await/async-closures/higher-ranked-return.stderr +++ b/tests/ui/async-await/async-closures/higher-ranked-return.stderr @@ -2,9 +2,9 @@ error: lifetime may not live long enough --> $DIR/higher-ranked-return.rs:11:46 | LL | let x = async move |x: &str| -> &str { - | ________________________________-________----_^ + | ________________________________-________-____^ | | | | - | | | return type of async closure `{async closure body@$DIR/higher-ranked-return.rs:11:46: 13:10}` contains a lifetime `'2` + | | | let's call the lifetime of this reference `'2` | | let's call the lifetime of this reference `'1` LL | | x LL | | }; diff --git a/tests/ui/async-await/async-closures/not-lending.stderr b/tests/ui/async-await/async-closures/not-lending.stderr index fb941502d3646..989b9ccb88a76 100644 --- a/tests/ui/async-await/async-closures/not-lending.stderr +++ b/tests/ui/async-await/async-closures/not-lending.stderr @@ -4,7 +4,7 @@ error: lifetime may not live long enough LL | let x = async move || -> &String { &s }; | ------------------------ ^^^^^^ returning this value requires that `'1` must outlive `'2` | | | - | | return type of async closure `{async closure body@$DIR/not-lending.rs:12:42: 12:48}` contains a lifetime `'2` + | | let's call the lifetime of this reference `'2` | lifetime `'1` represents this closure's body | = note: closure implements `AsyncFn`, so references to captured variables can't escape the closure @@ -15,7 +15,7 @@ error: lifetime may not live long enough LL | let x = async move || { &s }; | ------------- ^^^^^^ returning this value requires that `'1` must outlive `'2` | | | - | | return type of async closure `{async closure body@$DIR/not-lending.rs:16:31: 16:37}` contains a lifetime `'2` + | | return type of async closure is &'2 String | lifetime `'1` represents this closure's body | = note: closure implements `AsyncFn`, so references to captured variables can't escape the closure diff --git a/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr b/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr index c479adfa56d7e..3cf2d20f155fd 100644 --- a/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr +++ b/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr @@ -29,7 +29,7 @@ error: lifetime may not live long enough LL | (async move || { | ______-------------_^ | | | | - | | | return type of async closure `{async closure body@$DIR/issue-74072-lifetime-name-annotations.rs:13:20: 19:6}` contains a lifetime `'2` + | | | return type of async closure is &'2 i32 | | lifetime `'1` represents this closure's body LL | | LL | | @@ -78,7 +78,7 @@ error: lifetime may not live long enough LL | (async move || -> &i32 { | ______---------------------_^ | | | | - | | | return type of async closure `{async closure body@$DIR/issue-74072-lifetime-name-annotations.rs:23:28: 29:6}` contains a lifetime `'2` + | | | let's call the lifetime of this reference `'2` | | lifetime `'1` represents this closure's body LL | | LL | | From 38fa9b58d588fe013462ebf22736ede44d4e9fba Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Tue, 14 Jul 2026 10:57:16 -0300 Subject: [PATCH 15/22] Inherit eager invocation parents --- compiler/rustc_resolve/src/macros.rs | 34 +++++++++++++++++----------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 595c2fdf011dd..c4ed3609ab934 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -248,19 +248,30 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { force: bool, ) -> Result, Indeterminate> { let invoc_id = invoc.expansion_data.id; - let parent_scope = match self.invocation_parent_scopes.get(&invoc_id) { - Some(parent_scope) => *parent_scope, - None => { - // If there's no entry in the table, then we are resolving an eagerly expanded - // macro, which should inherit its parent scope from its eager expansion root - + let (parent_scope, invocation_parent) = match ( + self.invocation_parent_scopes.get(&invoc_id), + self.invocation_parents.get(&invoc_id), + ) { + (Some(parent_scope), Some(invocation_parent)) => (*parent_scope, *invocation_parent), + (None, None) => { + // Eager macro invocations are not collected into the reduced graph, so they + // inherit their parent scope and invocation parent from the eager expansion root - // the macro that requested this eager expansion. let parent_scope = *self .invocation_parent_scopes .get(&eager_expansion_root) .expect("non-eager expansion without a parent scope"); + let invocation_parent = *self + .invocation_parents + .get(&eager_expansion_root) + .expect("non-eager expansion without an invocation parent"); self.invocation_parent_scopes.insert(invoc_id, parent_scope); - parent_scope + self.invocation_parents.insert(invoc_id, invocation_parent); + (parent_scope, invocation_parent) } + _ => unreachable!( + "invocation parent tables must both contain or both miss an invocation" + ), }; let (mut derives, mut inner_attr, mut deleg_impl) = (&[][..], false, None); @@ -275,7 +286,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { InvocationKind::GlobDelegation { ref item, .. } => { let ast::AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() }; let DelegationSuffixes::Glob(star_span) = deleg.suffixes else { unreachable!() }; - deleg_impl = Some((self.invocation_parent(invoc_id), star_span)); + deleg_impl = Some((invocation_parent.parent_def, star_span)); // It is sufficient to consider glob delegation a bang macro for now. (&deleg.prefix, MacroKind::Bang) } @@ -286,16 +297,13 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let supports_macro_expansion = invoc.fragment_kind.supports_macro_expansion(); let node_id = invoc.expansion_data.lint_node_id; // This is a heuristic, but it's good enough for the lint. - let looks_like_invoc_in_mod_inert_attr = self - .invocation_parents - .get(&invoc_id) - .or_else(|| self.invocation_parents.get(&eager_expansion_root)) - .filter(|&&InvocationParent { parent_def: mod_def_id, in_attr, .. }| { + let looks_like_invoc_in_mod_inert_attr = Some(invocation_parent) + .filter(|&InvocationParent { parent_def: mod_def_id, in_attr, .. }| { in_attr && invoc.fragment_kind == AstFragmentKind::Expr && self.tcx.def_kind(mod_def_id) == DefKind::Mod }) - .map(|&InvocationParent { parent_def: mod_def_id, .. }| mod_def_id); + .map(|InvocationParent { parent_def: mod_def_id, .. }| mod_def_id); let sugg_span = match &invoc.kind { InvocationKind::Attr { item: Annotatable::Item(item), .. } if !item.span.from_expansion() => From c8fe362cd2aab1f4f044c25c7c3e14119459163b Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Tue, 14 Jul 2026 11:04:26 -0300 Subject: [PATCH 16/22] Add eager glob delegation regression test --- ...eager-format-glob-delegation-ice-159233.rs | 10 +++++++ ...r-format-glob-delegation-ice-159233.stderr | 27 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/ui/delegation/eager-format-glob-delegation-ice-159233.rs create mode 100644 tests/ui/delegation/eager-format-glob-delegation-ice-159233.stderr diff --git a/tests/ui/delegation/eager-format-glob-delegation-ice-159233.rs b/tests/ui/delegation/eager-format-glob-delegation-ice-159233.rs new file mode 100644 index 0000000000000..3c909b18dea5c --- /dev/null +++ b/tests/ui/delegation/eager-format-glob-delegation-ice-159233.rs @@ -0,0 +1,10 @@ +//@ edition: 2024 + +#![feature(fn_delegation)] + +static REGEX: () = format!(|| { reuse impl Trait for S; }); +//~^ ERROR format argument must be a string literal +//~| ERROR cannot find type `Trait` in this scope +//~| ERROR mismatched types + +fn main() {} diff --git a/tests/ui/delegation/eager-format-glob-delegation-ice-159233.stderr b/tests/ui/delegation/eager-format-glob-delegation-ice-159233.stderr new file mode 100644 index 0000000000000..0b644cbc7f879 --- /dev/null +++ b/tests/ui/delegation/eager-format-glob-delegation-ice-159233.stderr @@ -0,0 +1,27 @@ +error: format argument must be a string literal + --> $DIR/eager-format-glob-delegation-ice-159233.rs:5:28 + | +LL | static REGEX: () = format!(|| { reuse impl Trait for S; }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: you might be missing a string literal to format with + | +LL | static REGEX: () = format!("{}", || { reuse impl Trait for S; }); + | +++++ + +error[E0433]: cannot find type `Trait` in this scope + --> $DIR/eager-format-glob-delegation-ice-159233.rs:5:44 + | +LL | static REGEX: () = format!(|| { reuse impl Trait for S; }); + | ^^^^^ use of undeclared type `Trait` + +error[E0308]: mismatched types + --> $DIR/eager-format-glob-delegation-ice-159233.rs:5:20 + | +LL | static REGEX: () = format!(|| { reuse impl Trait for S; }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `String` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0433. +For more information about an error, try `rustc --explain E0308`. From 52f889196e445e51f84a7a0f76b0764a58db1e4e Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Tue, 14 Jul 2026 17:20:15 +0200 Subject: [PATCH 17/22] renovate: don't update PRs in the merge queue --- .github/renovate.json5 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 6d45e9a81b2fe..929334c561389 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -13,6 +13,8 @@ ], // Require manual approval from the Dependency Dashboard before opening PRs "dependencyDashboardApproval": true, + // Renovate shouldn't update a PR if it is in the bors merge queue. + "stopUpdatingLabel": "S-waiting-on-bors", "packageRules": [ { // No dashboard approval necessary for GitHub Actions updates From cbeec9144448bec4d6640ada09e1e3f48de9e72a Mon Sep 17 00:00:00 2001 From: apiraino Date: Tue, 14 Jul 2026 15:21:40 +0200 Subject: [PATCH 18/22] Fix Zulip backport command suggestion --- triagebot.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 9fd7bad75160f..e817e99ddf375 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -779,8 +779,8 @@ PR #{number} "{title}" fixes a regression and has been nominated for backport. This topic will help T-compiler getting context about it. Tip: to approve or decline from this Zulip thread, use: -@_**triagebot** backport approve beta #{number} -@_**triagebot** backport decline beta #{number} +@_**triagebot** backport approve +@_**triagebot** backport decline """, """\ /poll Should #{number} be beta backported? @@ -809,8 +809,8 @@ PR #{number} "{title}" fixes a regression and has been nominated for backport. This topic will help T-compiler getting context about it. Tip: to approve or decline from this Zulip thread, use: -@_**triagebot** backport approve stable #{number} -@_**triagebot** backport decline stable #{number} +@_**triagebot** backport approve +@_**triagebot** backport decline """, """\ /poll Approve stable backport of #{number}? From 5411bb0e2035e907d4f3cfbd8a9b5f6535d42ec0 Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Tue, 14 Jul 2026 22:54:52 +0300 Subject: [PATCH 19/22] std: clarify available_parallelism docs for Windows 11 processor groups GetSystemInfo reports only the primary processor group's CPU count. Before Windows 11 and Windows Server 2022 a process was confined to one group by default, so that count matched what it could use; since then processes span all groups by default, so it can undercount the parallelism available. --- library/std/src/thread/functions.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/library/std/src/thread/functions.rs b/library/std/src/thread/functions.rs index 70642108e1e01..21e7a2b2ed087 100644 --- a/library/std/src/thread/functions.rs +++ b/library/std/src/thread/functions.rs @@ -640,10 +640,13 @@ pub fn park_timeout(dur: Duration) { /// /// On Windows: /// - It may undercount the amount of parallelism available on systems with more -/// than 64 logical CPUs. However, programs typically need specific support to -/// take advantage of more than 64 logical CPUs, and in the absence of such -/// support, the number returned by this function accurately reflects the -/// number of logical CPUs the program can use by default. +/// than 64 logical CPUs, because it reports only the logical CPUs in one +/// processor group. Before Windows 11 and Windows Server 2022, a process was by +/// default confined to a single processor group, so this count reflected the CPUs +/// it could use without explicitly opting into other groups. Starting with Windows +/// 11 and Windows Server 2022, a process and its threads have affinities that by +/// default span all processor groups, so on systems with more than 64 logical CPUs +/// this may report fewer CPUs than are available to the program. /// - It may overcount the amount of parallelism available on systems limited by /// process-wide affinity masks, or job object limitations. /// From c72a794526efd186066c6bf166ba3c6b5af6a9bf Mon Sep 17 00:00:00 2001 From: Dominik Schwaiger Date: Tue, 14 Jul 2026 20:42:02 +0000 Subject: [PATCH 20/22] add rustc_no_writable to mem::forget and structs it uses * add rustc_no_writable no mem::forget and structs it uses * rename test file * apply #[rustc_no_writable] to transmute_prefix and transmute_neo --- library/core/src/mem/manually_drop.rs | 1 + library/core/src/mem/maybe_dangling.rs | 1 + library/core/src/mem/mod.rs | 3 +++ .../mem_forget-implicit_writes.rs | 20 +++++++++++++++++++ 4 files changed, 25 insertions(+) create mode 100644 src/tools/miri/tests/pass/tree_borrows/mem_forget-implicit_writes.rs diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index a70cb8ad67297..6c2f77a373393 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -178,6 +178,7 @@ impl ManuallyDrop { #[stable(feature = "manually_drop", since = "1.20.0")] #[rustc_const_stable(feature = "const_manually_drop", since = "1.32.0")] #[inline(always)] + #[rustc_no_writable] pub const fn new(value: T) -> ManuallyDrop { ManuallyDrop { value: MaybeDangling::new(value) } } diff --git a/library/core/src/mem/maybe_dangling.rs b/library/core/src/mem/maybe_dangling.rs index 7ae1bf899dd4f..46e5228a2da3f 100644 --- a/library/core/src/mem/maybe_dangling.rs +++ b/library/core/src/mem/maybe_dangling.rs @@ -74,6 +74,7 @@ pub struct MaybeDangling(P); impl MaybeDangling

{ /// Wraps a value in a `MaybeDangling`, allowing it to dangle. + #[rustc_no_writable] pub const fn new(x: P) -> Self where P: Sized, diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index a00a6c4783ba6..f79f461faf04c 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -186,6 +186,7 @@ pub mod type_info; #[rustc_const_stable(feature = "const_forget", since = "1.46.0")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "mem_forget"] +#[rustc_no_writable] pub const fn forget(t: T) { let _ = ManuallyDrop::new(t); } @@ -1170,6 +1171,7 @@ pub const unsafe fn transmute_copy(src: &Src) -> Dst { /// let _: std::mem::MaybeUninit = unsafe { transmute_prefix(123_u8) }; /// ``` #[unstable(feature = "transmute_prefix", issue = "155079")] +#[rustc_no_writable] pub const unsafe fn transmute_prefix(src: Src) -> Dst { #[repr(C)] union Transmute { @@ -1219,6 +1221,7 @@ pub const unsafe fn transmute_prefix(src: Src) -> Dst { #[unstable(feature = "transmute_neo", issue = "155079")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[inline] +#[rustc_no_writable] pub const unsafe fn transmute_neo(src: Src) -> Dst { const { assert!(Src::SIZE == Dst::SIZE) }; diff --git a/src/tools/miri/tests/pass/tree_borrows/mem_forget-implicit_writes.rs b/src/tools/miri/tests/pass/tree_borrows/mem_forget-implicit_writes.rs new file mode 100644 index 0000000000000..77cf6997761bb --- /dev/null +++ b/src/tools/miri/tests/pass/tree_borrows/mem_forget-implicit_writes.rs @@ -0,0 +1,20 @@ +// Regression test. This failed before applying `#[rustc_no_writable]` to `mem::forget`, `ManuallyDrop::new`, and `MaybeDangling::new`. +//@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes + +use std::mem; + +// This function is taken from the crate `derive_more`, from the file `into.rs` +unsafe fn transmute(from: From) -> To { + let to = unsafe { mem::transmute_copy(&from) }; + mem::forget(from); + to +} + +fn main() { + let mut val = 10u32; + let r: &mut u32 = &mut val; + + let to: &mut i32 = unsafe { transmute(r) }; + + assert_eq!(*to, 10); +} From 1a56799fef3e4746385da26590928c54a39520ca Mon Sep 17 00:00:00 2001 From: Krasimir Georgiev Date: Thu, 9 Jul 2026 10:57:52 +0000 Subject: [PATCH 21/22] cleanup: upstream dropped AMX-TF32 Co-authored-by: Gwen Mittertreiner --- compiler/rustc_target/src/target_features.rs | 1 - library/std_detect/src/detect/arch/x86.rs | 3 --- library/std_detect/src/detect/os/x86.rs | 1 - library/std_detect/tests/x86-specific.rs | 1 - tests/ui/check-cfg/target_feature.stderr | 1 - 5 files changed, 7 deletions(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 8c1ebee8416e5..fc25849b2d602 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -423,7 +423,6 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-tf32", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), ("apxf", Unstable(sym::apx_target_feature), &[]), ("avx", Stable, &["sse4.2"]), diff --git a/library/std_detect/src/detect/arch/x86.rs b/library/std_detect/src/detect/arch/x86.rs index ede4a80c088ed..e054546afcb19 100644 --- a/library/std_detect/src/detect/arch/x86.rs +++ b/library/std_detect/src/detect/arch/x86.rs @@ -91,7 +91,6 @@ features! { /// * `"amx-avx512"` /// * `"amx-fp8"` /// * `"amx-movrs"` - /// * `"amx-tf32"` /// * `"f16c"` /// * `"fma"` /// * `"bmi1"` @@ -228,8 +227,6 @@ features! { /// AMX-FP8 (Float8 Operations) @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_movrs: "amx-movrs"; /// AMX-MOVRS (Matrix MOVERS operations) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tf32: "amx-tf32"; - /// AMX-TF32 (TensorFloat32 Operations) @FEATURE: #[unstable(feature = "apx_target_feature", issue = "139284")] apxf: "apxf"; /// APX-F (Advanced Performance Extensions - Foundation) @FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_1: "avx10.1"; diff --git a/library/std_detect/src/detect/os/x86.rs b/library/std_detect/src/detect/os/x86.rs index 2b75cd6257087..4e739fb38d56e 100644 --- a/library/std_detect/src/detect/os/x86.rs +++ b/library/std_detect/src/detect/os/x86.rs @@ -216,7 +216,6 @@ pub(crate) fn detect_features() -> cache::Initializer { __cpuid_count(0x1e_u32, 1); enable(amx_feature_flags_eax, 4, Feature::amx_fp8); - enable(amx_feature_flags_eax, 6, Feature::amx_tf32); enable(amx_feature_flags_eax, 7, Feature::amx_avx512); enable(amx_feature_flags_eax, 8, Feature::amx_movrs); } diff --git a/library/std_detect/tests/x86-specific.rs b/library/std_detect/tests/x86-specific.rs index 4b02f78b7944e..117182558f46a 100644 --- a/library/std_detect/tests/x86-specific.rs +++ b/library/std_detect/tests/x86-specific.rs @@ -83,7 +83,6 @@ fn dump() { println!("widekl: {:?}", is_x86_feature_detected!("widekl")); println!("movrs: {:?}", is_x86_feature_detected!("movrs")); println!("amx-fp8: {:?}", is_x86_feature_detected!("amx-fp8")); - println!("amx-tf32: {:?}", is_x86_feature_detected!("amx-tf32")); println!("amx-avx512: {:?}", is_x86_feature_detected!("amx-avx512")); println!("amx-movrs: {:?}", is_x86_feature_detected!("amx-movrs")); } diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 98ba1de498bbe..eca0813e6a286 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -28,7 +28,6 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `amx-fp8` `amx-int8` `amx-movrs` -`amx-tf32` `amx-tile` `apxf` `atomics` From a05a2705f51fcc31eaeb9b1f2f6933a97eb5178b Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Tue, 14 Jul 2026 18:25:15 -0400 Subject: [PATCH 22/22] Add 1.97.1 release notes --- RELEASES.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 3146bdeb1b118..81310c20c5ef9 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,13 @@ +Version 1.97.1 (2026-07-16) +========================== + + + +- [rustc: Fix miscompilation in LLVM optimization](https://github.com/rust-lang/rust/issues/159035) + This backports an LLVM submodule bump to include the LLVM-side fix and a + revert of the rustc change that is one known trigger for the bug. The rustc + side revert should not be strictly necessary but is done out of abundance of caution. + Version 1.97.0 (2026-07-09) ==========================