From 6e002317cec5fa0c55a88307f839f2b5fb31922c Mon Sep 17 00:00:00 2001 From: Maksim Bondarenkov Date: Sat, 19 Jul 2025 14:19:54 +0300 Subject: [PATCH 01/16] opt-dist: rebuild rustc when doing static LLVM builds when building LLVM it's obvious that in case of shared build rustc doesn't need to be recompiled, but with static builds it would be better to compile rustc again to ensure we linked proper library. maybe I didn't understand the pipeline correctly, but it was strange for me to see that in Stage 5 LLVM is built while rustc is not --- src/tools/opt-dist/src/exec.rs | 6 ++++++ src/tools/opt-dist/src/main.rs | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs index 56eff2ca2a7df..e5a037dc4e9b2 100644 --- a/src/tools/opt-dist/src/exec.rs +++ b/src/tools/opt-dist/src/exec.rs @@ -189,6 +189,12 @@ impl Bootstrap { self } + /// Rebuild rustc in case of statically linked LLVM + pub fn rustc_rebuild(mut self) -> Self { + self.cmd = self.cmd.arg("--keep-stage").arg("0"); + self + } + pub fn run(self, timer: &mut TimerSection) -> anyhow::Result<()> { self.cmd.run()?; let metrics = load_metrics(&self.metrics_path)?; diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index 7857f196626bc..8dc7788361298 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -363,8 +363,14 @@ fn execute_pipeline( let mut dist = Bootstrap::dist(env, &dist_args) .llvm_pgo_optimize(llvm_pgo_profile.as_ref()) - .rustc_pgo_optimize(&rustc_pgo_profile) - .avoid_rustc_rebuild(); + .rustc_pgo_optimize(&rustc_pgo_profile); + + // if LLVM is not built we'll have PGO optimized rustc + dist = if env.supports_shared_llvm() || !env.build_llvm() { + dist.avoid_rustc_rebuild() + } else { + dist.rustc_rebuild() + }; for bolt_profile in bolt_profiles { dist = dist.with_bolt_profile(bolt_profile); From a6f266612a42737ee46b69645244975e75406b60 Mon Sep 17 00:00:00 2001 From: morinmorin Date: Fri, 25 Jul 2025 23:22:36 +0900 Subject: [PATCH 02/16] std/sys/fd: remove `- 1` from `READ_LIMIT` on Darwin Darwin's `read`/`write` syscalls emit `EINVAL` only when `nbyte > INT_MAX`. The case `nbyte == INT_MAX` is valid, so the subtraction can be removed. --- library/std/src/sys/fd/unix.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index cdca73cdca11e..a12f692e7543b 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -37,10 +37,10 @@ pub struct FileDesc(OwnedFd); // // On Apple targets however, apparently the 64-bit libc is either buggy or // intentionally showing odd behavior by rejecting any read with a size -// larger than or equal to INT_MAX. To handle both of these the read -// size is capped on both platforms. +// larger than INT_MAX. To handle both of these the read size is capped on +// both platforms. const READ_LIMIT: usize = if cfg!(target_vendor = "apple") { - libc::c_int::MAX as usize - 1 + libc::c_int::MAX as usize } else { libc::ssize_t::MAX as usize }; From 9449b786218d5c5b66222a774f445e1ccf3e3666 Mon Sep 17 00:00:00 2001 From: dianne Date: Sun, 10 Aug 2025 15:32:14 -0700 Subject: [PATCH 03/16] show a trailing comma on singleton tuple constructors in witness pats --- compiler/rustc_pattern_analysis/src/rustc/print.rs | 3 +++ .../ui/or-patterns/exhaustiveness-non-exhaustive.rs | 4 ++-- .../or-patterns/exhaustiveness-non-exhaustive.stderr | 12 ++++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/rustc/print.rs b/compiler/rustc_pattern_analysis/src/rustc/print.rs index 7649f72f8681b..022fac2a82642 100644 --- a/compiler/rustc_pattern_analysis/src/rustc/print.rs +++ b/compiler/rustc_pattern_analysis/src/rustc/print.rs @@ -119,6 +119,9 @@ pub(crate) fn write_struct_like<'tcx>( write!(f, "_")?; } } + if matches!(ty.kind(), ty::Tuple(..)) && num_fields == 1 { + write!(f, ",")?; + } write!(f, ")")?; } diff --git a/tests/ui/or-patterns/exhaustiveness-non-exhaustive.rs b/tests/ui/or-patterns/exhaustiveness-non-exhaustive.rs index 5999e04e0e2dd..9c0b26483849e 100644 --- a/tests/ui/or-patterns/exhaustiveness-non-exhaustive.rs +++ b/tests/ui/or-patterns/exhaustiveness-non-exhaustive.rs @@ -7,11 +7,11 @@ fn main() { (0 | 1, 2 | 3) => {} } match ((0u8,),) { - //~^ ERROR non-exhaustive patterns: `((4_u8..=u8::MAX))` + //~^ ERROR non-exhaustive patterns: `((4_u8..=u8::MAX,),)` ((0 | 1,) | (2 | 3,),) => {} } match (Some(0u8),) { - //~^ ERROR non-exhaustive patterns: `(Some(2_u8..=u8::MAX))` + //~^ ERROR non-exhaustive patterns: `(Some(2_u8..=u8::MAX),)` (None | Some(0 | 1),) => {} } } diff --git a/tests/ui/or-patterns/exhaustiveness-non-exhaustive.stderr b/tests/ui/or-patterns/exhaustiveness-non-exhaustive.stderr index 9f691aea8a79c..fe3a4aa2afc73 100644 --- a/tests/ui/or-patterns/exhaustiveness-non-exhaustive.stderr +++ b/tests/ui/or-patterns/exhaustiveness-non-exhaustive.stderr @@ -11,30 +11,30 @@ LL ~ (0 | 1, 2 | 3) => {}, LL + (2_u8..=u8::MAX, _) => todo!() | -error[E0004]: non-exhaustive patterns: `((4_u8..=u8::MAX))` not covered +error[E0004]: non-exhaustive patterns: `((4_u8..=u8::MAX,),)` not covered --> $DIR/exhaustiveness-non-exhaustive.rs:9:11 | LL | match ((0u8,),) { - | ^^^^^^^^^ pattern `((4_u8..=u8::MAX))` not covered + | ^^^^^^^^^ pattern `((4_u8..=u8::MAX,),)` not covered | = note: the matched value is of type `((u8,),)` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ ((0 | 1,) | (2 | 3,),) => {}, -LL + ((4_u8..=u8::MAX)) => todo!() +LL + ((4_u8..=u8::MAX,),) => todo!() | -error[E0004]: non-exhaustive patterns: `(Some(2_u8..=u8::MAX))` not covered +error[E0004]: non-exhaustive patterns: `(Some(2_u8..=u8::MAX),)` not covered --> $DIR/exhaustiveness-non-exhaustive.rs:13:11 | LL | match (Some(0u8),) { - | ^^^^^^^^^^^^ pattern `(Some(2_u8..=u8::MAX))` not covered + | ^^^^^^^^^^^^ pattern `(Some(2_u8..=u8::MAX),)` not covered | = note: the matched value is of type `(Option,)` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ (None | Some(0 | 1),) => {}, -LL + (Some(2_u8..=u8::MAX)) => todo!() +LL + (Some(2_u8..=u8::MAX),) => todo!() | error: aborting due to 3 previous errors From 8f649a7e585f03e69c61a5b8286a61b38307afe8 Mon Sep 17 00:00:00 2001 From: dianne Date: Sun, 10 Aug 2025 15:52:41 -0700 Subject: [PATCH 04/16] clean up witness printing for tuple-like constructors By construction, `subpatterns` contains all fields in order. Witness patterns are constructed with all fields in order by `WitnessPat::wild_from_ctor` and `WitnessStack::apply_constructor`, and the order is preserved at `write_struct_like`'s call-site in `print_witness_pat`. It's thus no longer necessary to go looking for fields or handle missing fields. --- .../rustc_pattern_analysis/src/rustc/print.rs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/rustc/print.rs b/compiler/rustc_pattern_analysis/src/rustc/print.rs index 022fac2a82642..cdf62c2553de1 100644 --- a/compiler/rustc_pattern_analysis/src/rustc/print.rs +++ b/compiler/rustc_pattern_analysis/src/rustc/print.rs @@ -101,23 +101,8 @@ pub(crate) fn write_struct_like<'tcx>( let num_fields = variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len()); if num_fields != 0 || variant_and_name.is_none() { write!(f, "(")?; - for i in 0..num_fields { - write!(f, "{}", start_or_comma())?; - - // Common case: the field is where we expect it. - if let Some(p) = subpatterns.get(i) { - if p.field.index() == i { - write!(f, "{}", p.pattern)?; - continue; - } - } - - // Otherwise, we have to go looking for it. - if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { - write!(f, "{}", p.pattern)?; - } else { - write!(f, "_")?; - } + for FieldPat { pattern, .. } in subpatterns { + write!(f, "{}{pattern}", start_or_comma())?; } if matches!(ty.kind(), ty::Tuple(..)) && num_fields == 1 { write!(f, ",")?; From c9ce45cb1e11408c2f0c7cdc89f22bf54406eb20 Mon Sep 17 00:00:00 2001 From: Karl Meakin Date: Sat, 16 Aug 2025 23:45:45 +0100 Subject: [PATCH 05/16] Optimize `char::encode_utf8` Save a few instructions in `encode_utf8_raw_unchecked` by performing manual CSE. --- library/core/src/char/methods.rs | 47 ++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 7ee0962721f5b..985e669c92d79 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1872,28 +1872,33 @@ pub const unsafe fn encode_utf8_raw_unchecked(code: u32, dst: *mut u8) { // SAFETY: The caller must guarantee that the buffer pointed to by `dst` // is at least `len` bytes long. unsafe { - match len { - 1 => { - *dst = code as u8; - } - 2 => { - *dst = (code >> 6 & 0x1F) as u8 | TAG_TWO_B; - *dst.add(1) = (code & 0x3F) as u8 | TAG_CONT; - } - 3 => { - *dst = (code >> 12 & 0x0F) as u8 | TAG_THREE_B; - *dst.add(1) = (code >> 6 & 0x3F) as u8 | TAG_CONT; - *dst.add(2) = (code & 0x3F) as u8 | TAG_CONT; - } - 4 => { - *dst = (code >> 18 & 0x07) as u8 | TAG_FOUR_B; - *dst.add(1) = (code >> 12 & 0x3F) as u8 | TAG_CONT; - *dst.add(2) = (code >> 6 & 0x3F) as u8 | TAG_CONT; - *dst.add(3) = (code & 0x3F) as u8 | TAG_CONT; - } - // SAFETY: `char` always takes between 1 and 4 bytes to encode in UTF-8. - _ => crate::hint::unreachable_unchecked(), + if len == 1 { + *dst = code as u8; + return; + } + + let last1 = (code >> 0 & 0x3F) as u8 | TAG_CONT; + let last2 = (code >> 6 & 0x3F) as u8 | TAG_CONT; + let last3 = (code >> 12 & 0x3F) as u8 | TAG_CONT; + let last4 = (code >> 18 & 0x3F) as u8 | TAG_FOUR_B; + + if len == 2 { + *dst = last2 | TAG_TWO_B; + *dst.add(1) = last1; + return; } + + if len == 3 { + *dst = last3 | TAG_THREE_B; + *dst.add(1) = last2; + *dst.add(2) = last1; + return; + } + + *dst = last4; + *dst.add(1) = last3; + *dst.add(2) = last2; + *dst.add(3) = last1; } } From e8ae1dab12bc1388cd5f6545ccc88297b779f511 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 21 Aug 2025 15:45:40 +0200 Subject: [PATCH 06/16] next-solver fix const_trait_impl bootstrap --- compiler/rustc_middle/src/ty/context.rs | 7 ++++-- .../src/solve/effect_goals.rs | 18 +++++++------ .../const-traits/constructor-const-fn.rs | 15 +++++++++++ .../const-traits/reservation-impl-ice.rs | 13 ++++++++++ .../const-traits/reservation-impl-ice.stderr | 25 +++++++++++++++++++ 5 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 tests/ui/traits/const-traits/constructor-const-fn.rs create mode 100644 tests/ui/traits/const-traits/reservation-impl-ice.rs create mode 100644 tests/ui/traits/const-traits/reservation-impl-ice.stderr diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 49a4733de3b0a..c215e44a9650b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -33,7 +33,7 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, LintEmitter, MultiSpan, }; use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::{CtorKind, DefKind}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState}; use rustc_hir::intravisit::VisitorExt; @@ -445,7 +445,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } fn fn_is_const(self, def_id: DefId) -> bool { - debug_assert_matches!(self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn); + debug_assert_matches!( + self.def_kind(def_id), + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(CtorOf::Struct, CtorKind::Fn) + ); self.is_conditionally_const(def_id) } diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 9a22bf58c0352..b7ae9994c6273 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -6,7 +6,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; -use rustc_type_ir::{self as ty, Interner, elaborate}; +use rustc_type_ir::{self as ty, Interner, TypingMode, elaborate}; use tracing::instrument; use super::assembly::{Candidate, structural_traits}; @@ -135,12 +135,16 @@ where } let impl_polarity = cx.impl_polarity(impl_def_id); - match impl_polarity { + let certainty = match impl_polarity { ty::ImplPolarity::Negative => return Err(NoSolution), - ty::ImplPolarity::Reservation => { - unimplemented!("reservation impl for const trait: {:?}", goal) - } - ty::ImplPolarity::Positive => {} + ty::ImplPolarity::Reservation => match ecx.typing_mode() { + TypingMode::Coherence => Certainty::AMBIGUOUS, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => return Err(NoSolution), + }, + ty::ImplPolarity::Positive => Certainty::Yes, }; if !cx.impl_is_const(impl_def_id) { @@ -171,7 +175,7 @@ where }); ecx.add_goals(GoalSource::ImplWhereBound, const_conditions); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.evaluate_added_goals_and_make_canonical_response(certainty) }) } diff --git a/tests/ui/traits/const-traits/constructor-const-fn.rs b/tests/ui/traits/const-traits/constructor-const-fn.rs new file mode 100644 index 0000000000000..4a7bf531df234 --- /dev/null +++ b/tests/ui/traits/const-traits/constructor-const-fn.rs @@ -0,0 +1,15 @@ +//@ check-pass +//@ compile-flags: -Znext-solver +#![feature(const_trait_impl)] +const fn impls_fn Foo>(_: &F) {} + +struct Foo(u32); + +const fn foo() { + // This previously triggered an incorrect assert + // when checking whether the constructor of `Foo` + // is const. + impls_fn(&Foo) +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/reservation-impl-ice.rs b/tests/ui/traits/const-traits/reservation-impl-ice.rs new file mode 100644 index 0000000000000..efaea1cc6b2bf --- /dev/null +++ b/tests/ui/traits/const-traits/reservation-impl-ice.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Znext-solver +#![feature(const_from, never_type, const_trait_impl)] + +const fn impls_from>() {} + +const fn foo() { + // This previously ICE'd when encountering the reservation impl + // from the standard library. + impls_from::<()>(); + //~^ ERROR the trait bound `(): From` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/reservation-impl-ice.stderr b/tests/ui/traits/const-traits/reservation-impl-ice.stderr new file mode 100644 index 0000000000000..a3fdcbac69e39 --- /dev/null +++ b/tests/ui/traits/const-traits/reservation-impl-ice.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `(): From` is not satisfied + --> $DIR/reservation-impl-ice.rs:9:18 + | +LL | impls_from::<()>(); + | ^^ the trait `From` is not implemented for `()` + | + = help: the following other types implement trait `From`: + `(T, T)` implements `From<[T; 2]>` + `(T, T, T)` implements `From<[T; 3]>` + `(T, T, T, T)` implements `From<[T; 4]>` + `(T, T, T, T, T)` implements `From<[T; 5]>` + `(T, T, T, T, T, T)` implements `From<[T; 6]>` + `(T, T, T, T, T, T, T)` implements `From<[T; 7]>` + `(T, T, T, T, T, T, T, T)` implements `From<[T; 8]>` + `(T, T, T, T, T, T, T, T, T)` implements `From<[T; 9]>` + and 4 others +note: required by a bound in `impls_from` + --> $DIR/reservation-impl-ice.rs:4:24 + | +LL | const fn impls_from>() {} + | ^^^^^^^^^^^^^^ required by this bound in `impls_from` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From df3d79793b9eab2320bb41b5473274d5aefd4dbc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 21 Aug 2025 21:51:20 +1000 Subject: [PATCH 07/16] Remove `dirs-sys-0.4.1` dependency. By updating rustfmt to use `dirs-6.0.0`. --- Cargo.lock | 20 ++++---------------- src/tools/rustfmt/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85df0dda81857..fb41728b99953 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1129,16 +1129,16 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "dirs-sys 0.5.0", + "dirs-sys", ] [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys", ] [[package]] @@ -1151,18 +1151,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.6", - "windows-sys 0.48.0", -] - [[package]] name = "dirs-sys" version = "0.5.0" diff --git a/src/tools/rustfmt/Cargo.toml b/src/tools/rustfmt/Cargo.toml index e497b79234202..6392ffbe409ff 100644 --- a/src/tools/rustfmt/Cargo.toml +++ b/src/tools/rustfmt/Cargo.toml @@ -40,7 +40,7 @@ cargo_metadata = "0.18" clap = { version = "4.4.2", features = ["derive"] } clap-cargo = "0.12.0" diff = "0.1" -dirs = "5.0" +dirs = "6.0" getopts = "0.2" ignore = "0.4" itertools = "0.12" From c54db96b8bc25f371b26d57e9d03f4ff5ffe293b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 21 Aug 2025 22:01:01 +1000 Subject: [PATCH 08/16] Remove `toml-0.5.11` dependency. We also depend on `toml-0.7.8` and `toml-0.8.23`, but this one is easy to get rid of. --- Cargo.lock | 13 ++----------- src/tools/build-manifest/Cargo.toml | 2 +- src/tools/bump-stage0/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb41728b99953..25ba977fd2ee0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -315,7 +315,7 @@ dependencies = [ "serde_json", "sha2", "tar", - "toml 0.5.11", + "toml 0.7.8", "xz2", ] @@ -336,7 +336,7 @@ dependencies = [ "curl", "indexmap", "serde", - "toml 0.5.11", + "toml 0.7.8", ] [[package]] @@ -5513,15 +5513,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "toml" version = "0.7.8" diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml index 7e0c4bee2b3cc..efa99f181b3a1 100644 --- a/src/tools/build-manifest/Cargo.toml +++ b/src/tools/build-manifest/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -toml = "0.5" +toml = "0.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" anyhow = "1.0.32" diff --git a/src/tools/bump-stage0/Cargo.toml b/src/tools/bump-stage0/Cargo.toml index 6ee7a831839f9..b7f3625da91b7 100644 --- a/src/tools/bump-stage0/Cargo.toml +++ b/src/tools/bump-stage0/Cargo.toml @@ -11,4 +11,4 @@ build_helper = { path = "../../build_helper" } curl = "0.4.38" indexmap = { version = "2.0.0", features = ["serde"] } serde = { version = "1.0.125", features = ["derive"] } -toml = "0.5.7" +toml = "0.7" From 78bdd86c676a4e08849677c351591db3564d87e8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 22 Aug 2025 09:22:02 +0200 Subject: [PATCH 09/16] miri: also detect aliasing of in-place argument and return place --- .../rustc_const_eval/src/interpret/step.rs | 23 +++++++++---- .../arg_inplace_locals_alias.rs | 2 ++ .../arg_inplace_locals_alias_ret.rs | 34 +++++++++++++++++++ .../arg_inplace_locals_alias_ret.stack.stderr | 25 ++++++++++++++ .../arg_inplace_locals_alias_ret.tree.stderr | 34 +++++++++++++++++++ 5 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs create mode 100644 src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.stack.stderr create mode 100644 src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 084d45cf2cbbf..23d362de308f3 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -2,6 +2,8 @@ //! //! The main entry point is the `step` method. +use std::iter; + use either::Either; use rustc_abi::{FIRST_VARIANT, FieldIdx}; use rustc_data_structures::fx::FxHashSet; @@ -426,6 +428,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { terminator: &mir::Terminator<'tcx>, func: &mir::Operand<'tcx>, args: &[Spanned>], + dest: &mir::Place<'tcx>, ) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> { let func = self.eval_operand(func, None)?; @@ -435,14 +438,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // protection, but then we'd force *a lot* of arguments into memory. So we do some syntactic // pre-processing here where if all `move` arguments are syntactically distinct local // variables (and none is indirect), we can skip the in-memory forcing. + // We have to include `dest` in that list so that we can detect aliasing of an in-place + // argument with the return place. let move_definitely_disjoint = 'move_definitely_disjoint: { let mut previous_locals = FxHashSet::::default(); - for arg in args { - let mir::Operand::Move(place) = arg.node else { - continue; // we can skip non-`Move` arguments. - }; + for place in args + .iter() + .filter_map(|a| { + // We only have to care about `Move` arguments. + if let mir::Operand::Move(place) = &a.node { Some(place) } else { None } + }) + .chain(iter::once(dest)) + { if place.is_indirect_first_projection() { - // An indirect `Move` argument could alias with anything else... + // An indirect in-place argument could alias with anything else... break 'move_definitely_disjoint false; } if !previous_locals.insert(place.local) { @@ -544,7 +553,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let old_loc = self.frame().loc; let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = - self.eval_callee_and_args(terminator, func, args)?; + self.eval_callee_and_args(terminator, func, args, &destination)?; let destination = self.eval_place(destination)?; self.init_fn_call( @@ -567,7 +576,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let old_frame_idx = self.frame_idx(); let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = - self.eval_callee_and_args(terminator, func, args)?; + self.eval_callee_and_args(terminator, func, args, &mir::Place::return_place())?; self.init_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?; diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs index b91a41d7650e1..744d64b9b1e88 100644 --- a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs @@ -1,3 +1,5 @@ +//! Ensure we detect aliasing of two in-place arguments for the tricky case where they do not +//! live in memory. //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows // Validation forces more things into memory, which we can't have here. diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs new file mode 100644 index 0000000000000..dff724f8d9657 --- /dev/null +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs @@ -0,0 +1,34 @@ +//! Ensure we detect aliasing of a in-place argument with the return place for the tricky case where +//! they do not live in memory. +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows +// Validation forces more things into memory, which we can't have here. +//@compile-flags: -Zmiri-disable-validation +#![feature(custom_mir, core_intrinsics)] +use std::intrinsics::mir::*; + +#[allow(unused)] +pub struct S(i32); + +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn main() { + mir! { + let _unit: (); + { + let staging = S(42); // This forces `staging` into memory... + let _non_copy = staging; // ... so we move it to a non-inmemory local here. + // This specifically uses a type with scalar representation to tempt Miri to use the + // efficient way of storing local variables (outside adressable memory). + Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue()) + //~[stack]^ ERROR: not granting access + //~[tree]| ERROR: /reborrow .* forbidden/ + } + after_call = { + Return() + } + } +} + +pub fn callee(x: S) -> S { + x +} diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.stack.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.stack.stderr new file mode 100644 index 0000000000000..fcd5b8752e7d2 --- /dev/null +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.stack.stderr @@ -0,0 +1,25 @@ +error: Undefined Behavior: not granting access to tag because that would remove [Unique for ] which is strongly protected + --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC + | +LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: was created here, as the root tag for ALLOC + --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC + | +LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: is this argument + --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC + | +LL | x + | ^ + = note: BACKTRACE (of the first span): + = note: inside `main` at tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr new file mode 100644 index 0000000000000..b7f514de0af98 --- /dev/null +++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr @@ -0,0 +1,34 @@ +error: Undefined Behavior: reborrow through (root of the allocation) at ALLOC[0x0] is forbidden + --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC + | +LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag (root of the allocation) is foreign to the protected tag (i.e., it is not a child) + = help: this reborrow (acting as a foreign read access) would cause the protected tag (currently Active) to become Disabled + = help: protected tags must never be Disabled +help: the accessed tag was created here + --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC + | +LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the protected tag was created here, in the initial state Reserved + --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC + | +LL | x + | ^ +help: the protected tag later transitioned to Active due to a child write access at offsets [0x0..0x4] + --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC + | +LL | x + | ^ + = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference + = note: BACKTRACE (of the first span): + = note: inside `main` at tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 7046ce89c666bb9e8695eb9b92721156c7b2b0ec Mon Sep 17 00:00:00 2001 From: Nia Espera Date: Wed, 16 Jul 2025 05:29:09 +0200 Subject: [PATCH 10/16] interpret/allocation: get_range on ProvenanceMap --- .../src/mir/interpret/allocation/provenance_map.rs | 11 +++++++++++ src/tools/miri/src/shims/native_lib/mod.rs | 11 +++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index ea8596ea28658..dbbd95408c8ab 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -120,6 +120,17 @@ impl ProvenanceMap { } } + /// Gets the provenances of all bytes (including from pointers) in a range. + pub fn get_range( + &self, + cx: &impl HasDataLayout, + range: AllocRange, + ) -> impl Iterator { + let ptr_provs = self.range_ptrs_get(range, cx).iter().map(|(_, p)| *p); + let byte_provs = self.range_bytes_get(range).iter().map(|(_, (p, _))| *p); + ptr_provs.chain(byte_provs) + } + /// Attempt to merge per-byte provenance back into ptr chunks, if the right fragments /// sit next to each other. Return `false` is that is not possible due to partial pointers. pub fn merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index e2a0bdbd9b408..74b9b704fea5e 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -242,14 +242,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { match evt { AccessEvent::Read(_) => { - // FIXME: ProvenanceMap should have something like get_range(). - let p_map = alloc.provenance(); - for idx in overlap { - // If a provenance was read by the foreign code, expose it. - if let Some((prov, _idx)) = p_map.get_byte(Size::from_bytes(idx), this) - { - this.expose_provenance(prov)?; - } + // If a provenance was read by the foreign code, expose it. + for prov in alloc.provenance().get_range(this, overlap.into()) { + this.expose_provenance(prov)?; } } AccessEvent::Write(_, certain) => { From b244f29777611e696854d8fd8d5313bb94dd1ce5 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 23 Aug 2025 07:30:27 +0530 Subject: [PATCH 11/16] remove default opts from config --- src/bootstrap/src/core/config/config.rs | 53 ------------------------- 1 file changed, 53 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index f579bdd847f66..84c9f9cc4d209 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -327,59 +327,6 @@ pub struct Config { } impl Config { - #[cfg_attr( - feature = "tracing", - instrument(target = "CONFIG_HANDLING", level = "trace", name = "Config::default_opts") - )] - pub fn default_opts() -> Config { - #[cfg(feature = "tracing")] - span!(target: "CONFIG_HANDLING", tracing::Level::TRACE, "constructing default config"); - - Config { - bypass_bootstrap_lock: false, - llvm_optimize: true, - ninja_in_file: true, - llvm_static_stdcpp: false, - llvm_libzstd: false, - backtrace: true, - rust_optimize: RustOptimize::Bool(true), - rust_optimize_tests: true, - rust_randomize_layout: false, - submodules: None, - docs: true, - docs_minification: true, - rust_rpath: true, - rust_strip: false, - channel: "dev".to_string(), - codegen_tests: true, - rust_dist_src: true, - rust_codegen_backends: vec![CodegenBackendKind::Llvm], - deny_warnings: true, - bindir: "bin".into(), - dist_include_mingw_linker: true, - dist_compression_profile: "fast".into(), - - stdout_is_tty: std::io::stdout().is_terminal(), - stderr_is_tty: std::io::stderr().is_terminal(), - - // set by build.rs - host_target: get_host_target(), - - src: { - let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - // Undo `src/bootstrap` - manifest_dir.parent().unwrap().parent().unwrap().to_owned() - }, - out: PathBuf::from("build"), - - // This is needed by codegen_ssa on macOS to ship `llvm-objcopy` aliased to - // `rust-objcopy` to workaround bad `strip`s on macOS. - llvm_tools_enabled: true, - - ..Default::default() - } - } - pub fn set_dry_run(&mut self, dry_run: DryRun) { self.exec_ctx.set_dry_run(dry_run); } From 32b193c248b2538d8397c74fb6a55aef1730a6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 23 Aug 2025 10:14:52 +0200 Subject: [PATCH 12/16] Remove profile section from Clippy To avoid workspace warnings. --- src/tools/clippy/Cargo.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 2add525b7e8ae..b3618932ded7d 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -65,13 +65,6 @@ harness = false name = "dogfood" harness = false -# quine-mc_cluskey makes up a significant part of the runtime in dogfood -# due to the number of conditions in the clippy_lints crate -# and enabling optimizations for that specific dependency helps a bit -# without increasing total build times. -[profile.dev.package.quine-mc_cluskey] -opt-level = 3 - [lints.rust.unexpected_cfgs] level = "warn" check-cfg = ['cfg(bootstrap)'] From 3ac32cace1df3edfd559c77bf2922c915253fbc0 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Fri, 22 Aug 2025 18:04:12 +0200 Subject: [PATCH 13/16] rustdoc: make attributes render consistently * make attributes render inside code elements and inside divs with class `code-attribute` * render attributes for macros, associated constants, and struct/union fields --- src/librustdoc/html/render/mod.rs | 37 +++---- src/librustdoc/html/render/print_item.rs | 100 +++++++++--------- src/librustdoc/html/templates/item_union.html | 1 - 3 files changed, 68 insertions(+), 70 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8d7f05775064f..673947ad30832 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1029,6 +1029,7 @@ fn assoc_const( ) -> impl fmt::Display { let tcx = cx.tcx(); fmt::from_fn(move |w| { + render_attributes_in_code(w, it, &" ".repeat(indent), cx); write!( w, "{indent}{vis}const {name}{generics}: {ty}", @@ -1136,10 +1137,10 @@ fn assoc_method( let (indent, indent_str, end_newline) = if parent == ItemType::Trait { header_len += 4; let indent_str = " "; - write!(w, "{}", render_attributes_in_pre(meth, indent_str, cx))?; + render_attributes_in_code(w, meth, indent_str, cx); (4, indent_str, Ending::NoNewline) } else { - render_attributes_in_code(w, meth, cx); + render_attributes_in_code(w, meth, "", cx); (0, "", Ending::Newline) }; write!( @@ -1309,28 +1310,28 @@ fn render_assoc_item( }) } -// When an attribute is rendered inside a `
` tag, it is formatted using
-// a whitespace prefix and newline.
-fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
-    fmt::from_fn(move |f| {
-        for a in it.attributes(cx.tcx(), cx.cache()) {
-            writeln!(f, "{prefix}{a}")?;
-        }
-        Ok(())
-    })
-}
-
 struct CodeAttribute(String);
 
-fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) {
-    write!(w, "
{}
", code_attr.0).unwrap(); +fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) { + write!( + w, + "
{prefix}{attr}
", + prefix = prefix, + attr = code_attr.0 + ) + .unwrap(); } // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. -fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) { +fn render_attributes_in_code( + w: &mut impl fmt::Write, + it: &clean::Item, + prefix: &str, + cx: &Context<'_>, +) { for attr in it.attributes(cx.tcx(), cx.cache()) { - render_code_attribute(CodeAttribute(attr), w); + render_code_attribute(prefix, CodeAttribute(attr), w); } } @@ -1342,7 +1343,7 @@ fn render_repr_attributes_in_code( item_type: ItemType, ) { if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) { - render_code_attribute(CodeAttribute(repr), w); + render_code_attribute("", CodeAttribute(repr), w); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 407238d66b8cd..2618ec272ca5a 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -20,8 +20,8 @@ use super::{ AssocItemLink, AssocItemRender, Context, ImplRenderingParameters, RenderMode, collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls, - render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, - render_impl, render_repr_attributes_in_code, render_rightside, render_stability_since_raw, + render_assoc_item, render_assoc_items, render_attributes_in_code, render_impl, + render_repr_attributes_in_code, render_rightside, render_stability_since_raw, render_stability_since_raw_with_extra, write_section_heading, }; use crate::clean; @@ -107,13 +107,6 @@ macro_rules! item_template_methods { } item_template_methods!($($rest)*); }; - (render_attributes_in_pre $($rest:tt)*) => { - fn render_attributes_in_pre(&self) -> impl fmt::Display { - let (item, cx) = self.item_and_cx(); - render_attributes_in_pre(item, "", cx) - } - item_template_methods!($($rest)*); - }; (render_assoc_items $($rest:tt)*) => { fn render_assoc_items(&self) -> impl fmt::Display { let (item, cx) = self.item_and_cx(); @@ -457,7 +450,12 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i write!( w, "\ - {vis}{imp}{stab_tags}\ + " + )?; + render_attributes_in_code(w, myitem, "", cx); + write!( + w, + "{vis}{imp}{stab_tags}\ ", vis = visibility_print_with_space(myitem, cx), imp = import.print(cx) @@ -625,11 +623,11 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp let notable_traits = notable_traits_button(&f.decl.output, cx).maybe_display(); wrap_item(w, |w| { + render_attributes_in_code(w, it, "", cx); write!( w, - "{attrs}{vis}{constness}{asyncness}{safety}{abi}fn \ + "{vis}{constness}{asyncness}{safety}{abi}fn \ {name}{generics}{decl}{notable_traits}{where_clause}", - attrs = render_attributes_in_pre(it, "", cx), vis = visibility, constness = constness, asyncness = asyncness, @@ -666,10 +664,10 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: // Output the trait definition wrap_item(w, |mut w| { + render_attributes_in_code(&mut w, it, "", cx); write!( w, - "{attrs}{vis}{safety}{is_auto}trait {name}{generics}{bounds}", - attrs = render_attributes_in_pre(it, "", cx), + "{vis}{safety}{is_auto}trait {name}{generics}{bounds}", vis = visibility_print_with_space(it, cx), safety = t.safety(tcx).print_with_space(), is_auto = if t.is_auto(tcx) { "auto " } else { "" }, @@ -1240,10 +1238,10 @@ fn item_trait_alias( ) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { + render_attributes_in_code(w, it, "", cx); write!( w, - "{attrs}trait {name}{generics} = {bounds}{where_clause};", - attrs = render_attributes_in_pre(it, "", cx), + "trait {name}{generics} = {bounds}{where_clause};", name = it.name.unwrap(), generics = t.generics.print(cx), bounds = print_bounds(&t.bounds, true, cx), @@ -1268,10 +1266,10 @@ fn item_trait_alias( fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { + render_attributes_in_code(w, it, "", cx); write!( w, - "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", - attrs = render_attributes_in_pre(it, "", cx), + "{vis}type {name}{generics}{where_clause} = {type_};", vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), generics = t.generics.print(cx), @@ -1452,7 +1450,14 @@ item_template!( impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { fn render_union(&self) -> impl Display { - render_union(self.it, Some(self.generics), self.fields, self.cx) + render_union( + self.it, + Some(self.generics), + self.fields, + self.def_id, + self.is_type_alias, + self.cx, + ) } fn document_field(&self, field: &'a clean::Item) -> impl Display { @@ -1479,27 +1484,6 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { _ => None, }) } - - fn render_attributes_in_pre(&self) -> impl fmt::Display { - fmt::from_fn(move |f| { - if self.is_type_alias { - // For now the only attributes we render for type aliases are `repr` attributes. - if let Some(repr) = clean::repr_attributes( - self.cx.tcx(), - self.cx.cache(), - self.def_id, - ItemType::Union, - ) { - writeln!(f, "{repr}")?; - }; - } else { - for a in self.it.attributes(self.cx.tcx(), self.cx.cache()) { - writeln!(f, "{a}")?; - } - } - Ok(()) - }) - } } fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display { @@ -1563,7 +1547,7 @@ impl<'clean> DisplayEnum<'clean> { // For now the only attributes we render for type aliases are `repr` attributes. render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum); } else { - render_attributes_in_code(w, it, cx); + render_attributes_in_code(w, it, "", cx); } write!( w, @@ -1702,7 +1686,7 @@ fn render_enum_fields( if v.is_stripped() { continue; } - write!(w, "{}", render_attributes_in_pre(v, TAB, cx))?; + render_attributes_in_code(w, v, TAB, cx); w.write_str(TAB)?; match v.kind { clean::VariantItem(ref var) => match var.kind { @@ -1882,6 +1866,7 @@ fn item_macro(cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) -> impl fmt: fmt::from_fn(|w| { wrap_item(w, |w| { // FIXME: Also print `#[doc(hidden)]` for `macro_rules!` if it `is_doc_hidden`. + render_attributes_in_code(w, it, "", cx); if !t.macro_rules { write!(w, "{}", visibility_print_with_space(it, cx))?; } @@ -1950,7 +1935,7 @@ fn item_constant( fmt::from_fn(|w| { wrap_item(w, |w| { let tcx = cx.tcx(); - render_attributes_in_code(w, it, cx); + render_attributes_in_code(w, it, "", cx); write!( w, @@ -2018,7 +2003,7 @@ impl<'a> DisplayStruct<'a> { // For now the only attributes we render for type aliases are `repr` attributes. render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct); } else { - render_attributes_in_code(w, it, cx); + render_attributes_in_code(w, it, "", cx); } write!( w, @@ -2115,7 +2100,7 @@ fn item_static( ) -> impl fmt::Display { fmt::from_fn(move |w| { wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); + render_attributes_in_code(w, it, "", cx); write!( w, "{vis}{safe}static {mutability}{name}: {typ}", @@ -2135,7 +2120,7 @@ fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display { fmt::from_fn(|w| { wrap_item(w, |w| { w.write_str("extern {\n")?; - render_attributes_in_code(w, it, cx); + render_attributes_in_code(w, it, "", cx); write!(w, " {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap(),) })?; @@ -2358,9 +2343,17 @@ fn render_union( it: &clean::Item, g: Option<&clean::Generics>, fields: &[clean::Item], + def_id: DefId, + is_type_alias: bool, cx: &Context<'_>, ) -> impl Display { fmt::from_fn(move |mut f| { + if is_type_alias { + // For now the only attributes we render for type aliases are `repr` attributes. + render_repr_attributes_in_code(f, cx, def_id, ItemType::Union); + } else { + render_attributes_in_code(f, it, "", cx); + } write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?; let where_displayed = if let Some(generics) = g { @@ -2390,6 +2383,7 @@ fn render_union( for field in fields { if let clean::StructFieldItem(ref ty) = field.kind { + render_attributes_in_code(&mut f, field, " ", cx); writeln!( f, " {}{}: {},", @@ -2481,11 +2475,15 @@ fn render_struct_fields( if toggle { toggle_open(&mut *w, format_args!("{count_fields} fields")); } + if has_visible_fields { + writeln!(w)?; + } for field in fields { if let clean::StructFieldItem(ref ty) = field.kind { - write!( + render_attributes_in_code(w, field, &format!("{tab} "), cx); + writeln!( w, - "\n{tab} {vis}{name}: {ty},", + "{tab} {vis}{name}: {ty},", vis = visibility_print_with_space(field, cx), name = field.name.unwrap(), ty = ty.print(cx) @@ -2495,12 +2493,12 @@ fn render_struct_fields( if has_visible_fields { if has_stripped_entries { - write!( + writeln!( w, - "\n{tab} /* private fields */" + "{tab} /* private fields */" )?; } - write!(w, "\n{tab}")?; + write!(w, "{tab}")?; } else if has_stripped_entries { write!(w, " /* private fields */ ")?; } diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index b5d3367a6a10b..5dba43ca255c7 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -1,5 +1,4 @@

-    {{ self.render_attributes_in_pre()|safe }}
     {{ self.render_union()|safe }}
 
{% if !self.is_type_alias %} From 90aa25a1c5dbae1e94099b1a2015dfb83783dbe0 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Fri, 22 Aug 2025 20:40:30 +0200 Subject: [PATCH 14/16] rustdoc: update attribute tests --- tests/rustdoc/attribute-rendering.rs | 3 +- tests/rustdoc/attributes.rs | 67 +++++++++++++++++-- ...ariant-non_exhaustive.type-alias-code.html | 4 +- ...enum-variant-non_exhaustive.type-code.html | 4 +- tests/rustdoc/type-alias/repr.rs | 3 +- 5 files changed, 71 insertions(+), 10 deletions(-) diff --git a/tests/rustdoc/attribute-rendering.rs b/tests/rustdoc/attribute-rendering.rs index bf9b81077f356..fb40d0a9887cf 100644 --- a/tests/rustdoc/attribute-rendering.rs +++ b/tests/rustdoc/attribute-rendering.rs @@ -1,7 +1,8 @@ #![crate_name = "foo"] //@ has 'foo/fn.f.html' -//@ has - //*[@'class="rust item-decl"]' '#[unsafe(export_name = "f")] pub fn f()' +//@ has - //*[@'class="code-attribute"]' '#[unsafe(export_name = "f")]' +//@ has - //*[@'class="rust item-decl"]' 'pub fn f()' #[unsafe(export_name = "\ f")] pub fn f() {} diff --git a/tests/rustdoc/attributes.rs b/tests/rustdoc/attributes.rs index 34487a8912778..db5f89ed46e4a 100644 --- a/tests/rustdoc/attributes.rs +++ b/tests/rustdoc/attributes.rs @@ -1,18 +1,77 @@ //@ edition: 2024 #![crate_name = "foo"] -//@ has foo/fn.f.html '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' +//@ has foo/fn.f.html '//*[@class="code-attribute"]' '#[unsafe(no_mangle)]' #[unsafe(no_mangle)] pub extern "C" fn f() {} -//@ has foo/fn.g.html '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "bar")]' +//@ has foo/fn.g.html '//*[@class="code-attribute"]' '#[unsafe(export_name = "bar")]' #[unsafe(export_name = "bar")] pub extern "C" fn g() {} -//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".text")]' +//@ has foo/fn.example.html '//*[@class="code-attribute"]' '#[unsafe(link_section = ".text")]' #[unsafe(link_section = ".text")] pub extern "C" fn example() {} -//@ has foo/struct.Repr.html '//pre[@class="rust item-decl"]' '#[repr(C, align(8))]' +//@ has foo/struct.Repr.html '//*[@class="code-attribute"]' '#[repr(C, align(8))]' #[repr(C, align(8))] pub struct Repr; + +//@ has foo/macro.macro_rule.html '//*[@class="code-attribute"]' '#[unsafe(link_section = ".text")]' +#[unsafe(link_section = ".text")] +#[macro_export] +macro_rules! macro_rule { + () => {}; +} + +//@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "enum")]' +#[unsafe(link_section = "enum")] +pub enum Enum { + //@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "a")]' + #[unsafe(link_section = "a")] + A, + //@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "quz")]' + #[unsafe(link_section = "quz")] + Quz { + //@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "b")]' + #[unsafe(link_section = "b")] + b: (), + }, +} + +//@ has 'foo/trait.Trait.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "trait")]' +#[unsafe(link_section = "trait")] +pub trait Trait { + //@ has 'foo/trait.Trait.html' + //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[unsafe(link_section = "bar")]' + //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' 'const BAR: u32 = 0u32' + #[unsafe(link_section = "bar")] + const BAR: u32 = 0; + + //@ has - '//*[@class="code-attribute"]' '#[unsafe(link_section = "foo")]' + #[unsafe(link_section = "foo")] + fn foo() {} +} + +//@ has 'foo/union.Union.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "union")]' +#[unsafe(link_section = "union")] +pub union Union { + //@ has 'foo/union.Union.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "x")]' + #[unsafe(link_section = "x")] + pub x: u32, + y: f32, +} + +//@ has 'foo/struct.Struct.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "struct")]' +#[unsafe(link_section = "struct")] +pub struct Struct { + //@ has 'foo/struct.Struct.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "x")]' + #[unsafe(link_section = "x")] + pub x: u32, + y: f32, +} + +// Check that the attributes from the trait items show up consistently in the impl. +//@ has 'foo/struct.Struct.html' '//*[@id="trait-implementations-list"]//*[@class="code-attribute"]' '#[unsafe(link_section = "bar")]' +//@ has 'foo/struct.Struct.html' '//*[@id="trait-implementations-list"]//*[@class="code-attribute"]' '#[unsafe(link_section = "foo")]' +impl Trait for Struct {} diff --git a/tests/rustdoc/enum/enum-variant-non_exhaustive.type-alias-code.html b/tests/rustdoc/enum/enum-variant-non_exhaustive.type-alias-code.html index 04eea709079d7..e8b8e93beb45e 100644 --- a/tests/rustdoc/enum/enum-variant-non_exhaustive.type-alias-code.html +++ b/tests/rustdoc/enum/enum-variant-non_exhaustive.type-alias-code.html @@ -1,4 +1,4 @@ pub enum TypeAlias { - #[non_exhaustive] +
#[non_exhaustive]
Variant, -}
\ No newline at end of file +}
diff --git a/tests/rustdoc/enum/enum-variant-non_exhaustive.type-code.html b/tests/rustdoc/enum/enum-variant-non_exhaustive.type-code.html index 6c8851ea5df47..51763c824ebd2 100644 --- a/tests/rustdoc/enum/enum-variant-non_exhaustive.type-code.html +++ b/tests/rustdoc/enum/enum-variant-non_exhaustive.type-code.html @@ -1,4 +1,4 @@ pub enum Type { - #[non_exhaustive] +
#[non_exhaustive]
Variant, -}
\ No newline at end of file +} diff --git a/tests/rustdoc/type-alias/repr.rs b/tests/rustdoc/type-alias/repr.rs index cf90798036099..884ed74264a58 100644 --- a/tests/rustdoc/type-alias/repr.rs +++ b/tests/rustdoc/type-alias/repr.rs @@ -22,7 +22,8 @@ pub union Foo2 { } //@ has 'foo/type.Bar2.html' -//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]\npub union Bar2 \{*' +//@ matches - '//*[@class="code-attribute"]' '#\[repr\(C\)\]' +//@ matches - '//*[@class="rust item-decl"]' 'pub union Bar2 \{*' // Ensures that we see the doc comment of the type alias and not of the aliased type. //@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar' /// bar From 1c03ae19dba16ffa95394927a4d91d30fc284910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 20 Aug 2025 16:49:11 +0200 Subject: [PATCH 15/16] port attribute to the new parsing infrastructure --- .../src/attributes/codegen_attrs.rs | 113 ++++++++++++++++- compiler/rustc_attr_parsing/src/context.rs | 4 +- compiler/rustc_codegen_ssa/messages.ftl | 3 - .../rustc_codegen_ssa/src/codegen_attrs.rs | 89 +++----------- compiler/rustc_codegen_ssa/src/errors.rs | 8 -- .../rustc_hir/src/attrs/data_structures.rs | 7 ++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir/src/attrs/pretty_printing.rs | 13 +- compiler/rustc_passes/src/check_attr.rs | 74 +++++------ tests/ui/attributes/malformed-attrs.stderr | 36 +++--- tests/ui/sanitize-attr/invalid-sanitize.rs | 9 +- .../ui/sanitize-attr/invalid-sanitize.stderr | 115 +++++++++++------- 12 files changed, 281 insertions(+), 191 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 91053811a0b8c..b884f8f38328c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,4 +1,4 @@ -use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, UsedBy}; +use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy}; use rustc_session::parse::feature_err; use super::prelude::*; @@ -464,6 +464,12 @@ impl CombineAttributeParser for ForceTargetFeatureParser { was_forced: true, }; const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, @@ -471,11 +477,106 @@ impl CombineAttributeParser for ForceTargetFeatureParser { ) -> impl IntoIterator + 'c { parse_tf_attribute(cx, args) } +} - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ - Allow(Target::Fn), - Allow(Target::Method(MethodKind::Inherent)), - Allow(Target::Method(MethodKind::Trait { body: true })), - Allow(Target::Method(MethodKind::TraitImpl)), +pub(crate) struct SanitizeParser; + +impl SingleAttributeParser for SanitizeParser { + const PATH: &[Symbol] = &[sym::sanitize]; + + // FIXME: still checked in check_attrs.rs + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + const TEMPLATE: AttributeTemplate = template!(List: &[ + r#"address = "on|off""#, + r#"kernel_address = "on|off""#, + r#"cfi = "on|off""#, + r#"hwaddress = "on|off""#, + r#"kcfi = "on|off""#, + r#"memory = "on|off""#, + r#"memtag = "on|off""#, + r#"shadow_call_stack = "on|off""#, + r#"thread = "on|off""# ]); + + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + + let mut on_set = SanitizerSet::empty(); + let mut off_set = SanitizerSet::empty(); + + for item in list.mixed() { + let Some(item) = item.meta_item() else { + cx.expected_name_value(item.span(), None); + continue; + }; + + let path = item.path().word_sym(); + let Some(value) = item.args().name_value() else { + cx.expected_name_value(item.span(), path); + continue; + }; + + let mut apply = |s: SanitizerSet| { + let is_on = match value.value_as_str() { + Some(sym::on) => true, + Some(sym::off) => false, + Some(_) => { + cx.expected_specific_argument_strings( + value.value_span, + &[sym::on, sym::off], + ); + return; + } + None => { + cx.expected_string_literal(value.value_span, Some(value.value_as_lit())); + return; + } + }; + + if is_on { + on_set |= s; + } else { + off_set |= s; + } + }; + + match path { + Some(sym::address) | Some(sym::kernel_address) => { + apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS) + } + Some(sym::cfi) => apply(SanitizerSet::CFI), + Some(sym::kcfi) => apply(SanitizerSet::KCFI), + Some(sym::memory) => apply(SanitizerSet::MEMORY), + Some(sym::memtag) => apply(SanitizerSet::MEMTAG), + Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK), + Some(sym::thread) => apply(SanitizerSet::THREAD), + Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS), + _ => { + cx.expected_specific_argument_strings( + item.path().span(), + &[ + sym::address, + sym::cfi, + sym::kcfi, + sym::memory, + sym::memtag, + sym::shadow_call_stack, + sym::thread, + sym::hwaddress, + ], + ); + continue; + } + } + } + + Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span }) + } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b9c415e8085f7..bb701df6053c6 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -20,7 +20,8 @@ use crate::attributes::allow_unstable::{ use crate::attributes::body::CoroutineParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, - NoMangleParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, + NoMangleParser, OptimizeParser, SanitizeParser, TargetFeatureParser, TrackCallerParser, + UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; @@ -184,6 +185,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 42ba01541920b..44b9941691a6b 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -171,9 +171,6 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol = invalid monomorphizati codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` of size `{$size}` to `{$ret_ty}` -codegen_ssa_invalid_sanitize = invalid argument for `sanitize` - .note = expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread` - codegen_ssa_invalid_windows_subsystem = invalid windows subsystem `{$subsystem}`, only `windows` and `console` are allowed codegen_ssa_ld64_unimplemented_modifier = `as-needed` modifier not implemented yet for ld64 diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 23e2abd6de3a6..6b0bd64102c65 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -293,6 +293,9 @@ fn process_builtin_attrs( codegen_fn_attrs.linkage = linkage; } } + AttributeKind::Sanitize { span, .. } => { + interesting_spans.sanitize = Some(*span); + } _ => {} } } @@ -310,7 +313,6 @@ fn process_builtin_attrs( codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, - sym::sanitize => interesting_spans.sanitize = Some(attr.span()), sym::instruction_set => { codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr) } @@ -560,79 +562,9 @@ fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { } } -/// For an attr that has the `sanitize` attribute, read the list of -/// disabled sanitizers. `current_attr` holds the information about -/// previously parsed attributes. -fn parse_sanitize_attr( - tcx: TyCtxt<'_>, - attr: &Attribute, - current_attr: SanitizerSet, -) -> SanitizerSet { - let mut result = current_attr; - if let Some(list) = attr.meta_item_list() { - for item in list.iter() { - let MetaItemInner::MetaItem(set) = item else { - tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() }); - break; - }; - let segments = set.path.segments.iter().map(|x| x.ident.name).collect::>(); - match segments.as_slice() { - // Similar to clang, sanitize(address = ..) and - // sanitize(kernel_address = ..) control both ASan and KASan - // Source: https://reviews.llvm.org/D44981. - [sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::off) => { - result |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS - } - [sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::on) => { - result &= !SanitizerSet::ADDRESS; - result &= !SanitizerSet::KERNELADDRESS; - } - [sym::cfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::CFI, - [sym::cfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::CFI, - [sym::kcfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::KCFI, - [sym::kcfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::KCFI, - [sym::memory] if set.value_str() == Some(sym::off) => { - result |= SanitizerSet::MEMORY - } - [sym::memory] if set.value_str() == Some(sym::on) => { - result &= !SanitizerSet::MEMORY - } - [sym::memtag] if set.value_str() == Some(sym::off) => { - result |= SanitizerSet::MEMTAG - } - [sym::memtag] if set.value_str() == Some(sym::on) => { - result &= !SanitizerSet::MEMTAG - } - [sym::shadow_call_stack] if set.value_str() == Some(sym::off) => { - result |= SanitizerSet::SHADOWCALLSTACK - } - [sym::shadow_call_stack] if set.value_str() == Some(sym::on) => { - result &= !SanitizerSet::SHADOWCALLSTACK - } - [sym::thread] if set.value_str() == Some(sym::off) => { - result |= SanitizerSet::THREAD - } - [sym::thread] if set.value_str() == Some(sym::on) => { - result &= !SanitizerSet::THREAD - } - [sym::hwaddress] if set.value_str() == Some(sym::off) => { - result |= SanitizerSet::HWADDRESS - } - [sym::hwaddress] if set.value_str() == Some(sym::on) => { - result &= !SanitizerSet::HWADDRESS - } - _ => { - tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() }); - } - } - } - } - result -} - fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { // Backtrack to the crate root. - let disabled = match tcx.opt_local_parent(did) { + let mut disabled = match tcx.opt_local_parent(did) { // Check the parent (recursively). Some(parent) => tcx.disabled_sanitizers_for(parent), // We reached the crate root without seeing an attribute, so @@ -641,8 +573,17 @@ fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { }; // Check for a sanitize annotation directly on this def. - if let Some(attr) = tcx.get_attr(did, sym::sanitize) { - return parse_sanitize_attr(tcx, attr, disabled); + if let Some((on_set, off_set)) = find_attr!(tcx.get_all_attrs(did), AttributeKind::Sanitize {on_set, off_set, ..} => (on_set, off_set)) + { + // the on set is the set of sanitizers explicitly enabled. + // we mask those out since we want the set of disabled sanitizers here + disabled &= !*on_set; + // the off set is the set of sanitizers explicitly disabled. + // we or those in here. + disabled |= *off_set; + // the on set and off set are distjoint since there's a third option: unset. + // a node may not set the sanitizer setting in which case it inherits from parents. + // the code above in this function does this backtracking } disabled } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 209c78ddeda4c..fb5a82051405d 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1120,14 +1120,6 @@ impl IntoDiagArg for ExpectedPointerMutability { } } -#[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_sanitize)] -#[note] -pub(crate) struct InvalidSanitize { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_target_feature_safe_trait)] pub(crate) struct TargetFeatureSafeTrait { diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 2209b18df3fa1..1e73bb6f5fd26 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -10,6 +10,7 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; use rustc_span::{Ident, Span, Symbol}; +pub use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; use crate::attrs::pretty_printing::PrintAttribute; @@ -505,6 +506,12 @@ pub enum AttributeKind { /// Represents `#[rustc_object_lifetime_default]`. RustcObjectLifetimeDefault, + /// Represents `#[sanitize]` + /// + /// the on set and off set are distjoint since there's a third option: unset. + /// a node may not set the sanitizer setting in which case it inherits from parents. + Sanitize { on_set: SanitizerSet, off_set: SanitizerSet, span: Span }, + /// Represents `#[should_panic]` ShouldPanic { reason: Option, span: Span }, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 485ded3981fc2..77c6faa7acd9f 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -73,6 +73,7 @@ impl AttributeKind { RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, + Sanitize { .. } => No, ShouldPanic { .. } => No, SkipDuringMethodDispatch { .. } => No, SpecializationTrait(..) => No, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index e44b29141da9a..e65de25b45117 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -6,6 +6,7 @@ use rustc_ast::{AttrStyle, IntTy, UintTy}; use rustc_ast_pretty::pp::Printer; use rustc_span::hygiene::Transparency; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; +use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; /// This trait is used to print attributes in `rustc_hir_pretty`. @@ -146,4 +147,14 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); print_disp!(u16, bool, NonZero); -print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); +print_debug!( + Symbol, + Ident, + UintTy, + IntTy, + Align, + AttrStyle, + CommentKind, + Transparency, + SanitizerSet, +); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 077afd6dd2371..c610ce4fc8599 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -18,7 +18,7 @@ use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, }; -use rustc_hir::attrs::{AttributeKind, InlineAttr, MirDialect, MirPhase, ReprAttr}; +use rustc_hir::attrs::{AttributeKind, InlineAttr, MirDialect, MirPhase, ReprAttr, SanitizerSet}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -197,6 +197,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::CustomMir(dialect, phase, attr_span)) => { self.check_custom_mir(dialect, phase, attr_span) } + &Attribute::Parsed(AttributeKind::Sanitize { on_set, off_set, span: attr_span}) => { + self.check_sanitize(attr_span, on_set | off_set, span, target); + }, Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -258,9 +261,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } - [sym::sanitize, ..] => { - self.check_sanitize(attr, span, target) - } [sym::thread_local, ..] => self.check_thread_local(attr, span, target), [sym::doc, ..] => self.check_doc_attrs( attr, @@ -483,42 +483,48 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that the `#[sanitize(..)]` attribute is applied to a /// function/closure/method, or to an impl block or module. - fn check_sanitize(&self, attr: &Attribute, target_span: Span, target: Target) { + fn check_sanitize( + &self, + attr_span: Span, + set: SanitizerSet, + target_span: Span, + target: Target, + ) { let mut not_fn_impl_mod = None; let mut no_body = None; - if let Some(list) = attr.meta_item_list() { - for item in list.iter() { - let MetaItemInner::MetaItem(set) = item else { - return; - }; - let segments = set.path.segments.iter().map(|x| x.ident.name).collect::>(); - match target { - Target::Fn - | Target::Closure - | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) - | Target::Impl { .. } - | Target::Mod => return, - Target::Static if matches!(segments.as_slice(), [sym::address]) => return, - - // These are "functions", but they aren't allowed because they don't - // have a body, so the usual explanation would be confusing. - Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { - no_body = Some(target_span); - } + match target { + Target::Fn + | Target::Closure + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) + | Target::Impl { .. } + | Target::Mod => return, + Target::Static + // if we mask out the address bits, i.e. *only* address was set, + // we allow it + if set & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS) + == SanitizerSet::empty() => + { + return; + } - _ => { - not_fn_impl_mod = Some(target_span); - } - } + // These are "functions", but they aren't allowed because they don't + // have a body, so the usual explanation would be confusing. + Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { + no_body = Some(target_span); + } + + _ => { + not_fn_impl_mod = Some(target_span); } - self.dcx().emit_err(errors::SanitizeAttributeNotAllowed { - attr_span: attr.span(), - not_fn_impl_mod, - no_body, - help: (), - }); } + + self.dcx().emit_err(errors::SanitizeAttributeNotAllowed { + attr_span, + not_fn_impl_mod, + no_body, + help: (), + }); } /// Checks if `#[naked]` is applied to a function definition. diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 98ff578918b04..11e01ac29be23 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -49,24 +49,6 @@ LL | #[crate_name] | = note: for more information, visit -error: malformed `sanitize` attribute input - --> $DIR/malformed-attrs.rs:92:1 - | -LL | #[sanitize] - | ^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL | #[sanitize(address = "on|off")] - | ++++++++++++++++++++ -LL | #[sanitize(cfi = "on|off")] - | ++++++++++++++++ -LL | #[sanitize(hwaddress = "on|off")] - | ++++++++++++++++++++++ -LL | #[sanitize(kcfi = "on|off")] - | +++++++++++++++++ - = and 5 other candidates - error: malformed `instruction_set` attribute input --> $DIR/malformed-attrs.rs:106:1 | @@ -543,6 +525,24 @@ LL | #[coverage(off)] LL | #[coverage(on)] | ++++ +error[E0539]: malformed `sanitize` attribute input + --> $DIR/malformed-attrs.rs:92:1 + | +LL | #[sanitize] + | ^^^^^^^^^^^ expected this to be a list + | +help: try changing it to one of the following valid forms of the attribute + | +LL | #[sanitize(address = "on|off")] + | ++++++++++++++++++++ +LL | #[sanitize(cfi = "on|off")] + | ++++++++++++++++ +LL | #[sanitize(hwaddress = "on|off")] + | ++++++++++++++++++++++ +LL | #[sanitize(kcfi = "on|off")] + | +++++++++++++++++ + = and 5 other candidates + error[E0565]: malformed `no_implicit_prelude` attribute input --> $DIR/malformed-attrs.rs:97:1 | diff --git a/tests/ui/sanitize-attr/invalid-sanitize.rs b/tests/ui/sanitize-attr/invalid-sanitize.rs index 49dc01c8daaf4..957ce780ad0fd 100644 --- a/tests/ui/sanitize-attr/invalid-sanitize.rs +++ b/tests/ui/sanitize-attr/invalid-sanitize.rs @@ -1,8 +1,7 @@ #![feature(sanitize)] -#[sanitize(brontosaurus = "off")] //~ ERROR invalid argument -fn main() { -} +#[sanitize(brontosaurus = "off")] //~ ERROR malformed `sanitize` attribute input +fn main() {} #[sanitize(address = "off")] //~ ERROR multiple `sanitize` attributes #[sanitize(address = "off")] @@ -12,11 +11,11 @@ fn multiple_consistent() {} #[sanitize(address = "off")] fn multiple_inconsistent() {} -#[sanitize(address = "bogus")] //~ ERROR invalid argument for `sanitize` +#[sanitize(address = "bogus")] //~ ERROR malformed `sanitize` attribute input fn wrong_value() {} #[sanitize = "off"] //~ ERROR malformed `sanitize` attribute input -fn name_value () {} +fn name_value() {} #[sanitize] //~ ERROR malformed `sanitize` attribute input fn just_word() {} diff --git a/tests/ui/sanitize-attr/invalid-sanitize.stderr b/tests/ui/sanitize-attr/invalid-sanitize.stderr index 4bf81770b89c0..ec0a93be1420e 100644 --- a/tests/ui/sanitize-attr/invalid-sanitize.stderr +++ b/tests/ui/sanitize-attr/invalid-sanitize.stderr @@ -1,82 +1,115 @@ -error: malformed `sanitize` attribute input - --> $DIR/invalid-sanitize.rs:18:1 +error[E0539]: malformed `sanitize` attribute input + --> $DIR/invalid-sanitize.rs:3:1 | -LL | #[sanitize = "off"] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[sanitize(brontosaurus = "off")] + | ^^^^^^^^^^^------------^^^^^^^^^^ + | | + | valid arguments are "address", "cfi", "kcfi", "memory", "memtag", "shadow_call_stack", "thread" or "hwaddress" | -help: the following are the possible correct uses +help: try changing it to one of the following valid forms of the attribute | -LL - #[sanitize = "off"] +LL - #[sanitize(brontosaurus = "off")] LL + #[sanitize(address = "on|off")] | -LL - #[sanitize = "off"] +LL - #[sanitize(brontosaurus = "off")] LL + #[sanitize(cfi = "on|off")] | -LL - #[sanitize = "off"] +LL - #[sanitize(brontosaurus = "off")] LL + #[sanitize(hwaddress = "on|off")] | -LL - #[sanitize = "off"] +LL - #[sanitize(brontosaurus = "off")] LL + #[sanitize(kcfi = "on|off")] | = and 5 other candidates -error: malformed `sanitize` attribute input - --> $DIR/invalid-sanitize.rs:21:1 - | -LL | #[sanitize] - | ^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL | #[sanitize(address = "on|off")] - | ++++++++++++++++++++ -LL | #[sanitize(cfi = "on|off")] - | ++++++++++++++++ -LL | #[sanitize(hwaddress = "on|off")] - | ++++++++++++++++++++++ -LL | #[sanitize(kcfi = "on|off")] - | +++++++++++++++++ - = and 5 other candidates - error: multiple `sanitize` attributes - --> $DIR/invalid-sanitize.rs:7:1 + --> $DIR/invalid-sanitize.rs:6:1 | LL | #[sanitize(address = "off")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-sanitize.rs:8:1 + --> $DIR/invalid-sanitize.rs:7:1 | LL | #[sanitize(address = "off")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: multiple `sanitize` attributes - --> $DIR/invalid-sanitize.rs:11:1 + --> $DIR/invalid-sanitize.rs:10:1 | LL | #[sanitize(address = "on")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-sanitize.rs:12:1 + --> $DIR/invalid-sanitize.rs:11:1 | LL | #[sanitize(address = "off")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid argument for `sanitize` - --> $DIR/invalid-sanitize.rs:3:1 +error[E0539]: malformed `sanitize` attribute input + --> $DIR/invalid-sanitize.rs:14:1 | -LL | #[sanitize(brontosaurus = "off")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[sanitize(address = "bogus")] + | ^^^^^^^^^^^^^^^^^^^^^-------^^ + | | + | valid arguments are "on" or "off" + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[sanitize(address = "bogus")] +LL + #[sanitize(address = "on|off")] + | +LL - #[sanitize(address = "bogus")] +LL + #[sanitize(cfi = "on|off")] + | +LL - #[sanitize(address = "bogus")] +LL + #[sanitize(hwaddress = "on|off")] + | +LL - #[sanitize(address = "bogus")] +LL + #[sanitize(kcfi = "on|off")] | - = note: expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread` + = and 5 other candidates -error: invalid argument for `sanitize` - --> $DIR/invalid-sanitize.rs:15:1 +error[E0539]: malformed `sanitize` attribute input + --> $DIR/invalid-sanitize.rs:17:1 | -LL | #[sanitize(address = "bogus")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[sanitize = "off"] + | ^^^^^^^^^^^^^^^^^^^ expected this to be a list + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[sanitize = "off"] +LL + #[sanitize(address = "on|off")] + | +LL - #[sanitize = "off"] +LL + #[sanitize(cfi = "on|off")] + | +LL - #[sanitize = "off"] +LL + #[sanitize(hwaddress = "on|off")] + | +LL - #[sanitize = "off"] +LL + #[sanitize(kcfi = "on|off")] + | + = and 5 other candidates + +error[E0539]: malformed `sanitize` attribute input + --> $DIR/invalid-sanitize.rs:20:1 | - = note: expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread` +LL | #[sanitize] + | ^^^^^^^^^^^ expected this to be a list + | +help: try changing it to one of the following valid forms of the attribute + | +LL | #[sanitize(address = "on|off")] + | ++++++++++++++++++++ +LL | #[sanitize(cfi = "on|off")] + | ++++++++++++++++ +LL | #[sanitize(hwaddress = "on|off")] + | ++++++++++++++++++++++ +LL | #[sanitize(kcfi = "on|off")] + | +++++++++++++++++ + = and 5 other candidates error: aborting due to 6 previous errors +For more information about this error, try `rustc --explain E0539`. From d8b40bdb5ac4a2cb078de2f0ba95144e4b026a6c Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 23 Aug 2025 13:08:07 +0200 Subject: [PATCH 16/16] citool: cleanup `mismatched_lifetime_syntaxes` warnings --- src/ci/citool/src/analysis.rs | 2 +- src/ci/citool/src/test_dashboard.rs | 2 +- src/ci/citool/src/utils.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ci/citool/src/analysis.rs b/src/ci/citool/src/analysis.rs index 62974be2dbe8c..8ba8f1ab564b2 100644 --- a/src/ci/citool/src/analysis.rs +++ b/src/ci/citool/src/analysis.rs @@ -75,7 +75,7 @@ fn format_build_step_diffs(current: &BuildStep, parent: &BuildStep) -> String { } } - fn get_steps(step: &BuildStep) -> Vec { + fn get_steps(step: &BuildStep) -> Vec> { step.linearize_steps().into_iter().map(|v| StepByName(v)).collect() } diff --git a/src/ci/citool/src/test_dashboard.rs b/src/ci/citool/src/test_dashboard.rs index 8fbd0d3f200d4..c9de38852e5a8 100644 --- a/src/ci/citool/src/test_dashboard.rs +++ b/src/ci/citool/src/test_dashboard.rs @@ -33,7 +33,7 @@ fn write_page(dir: &Path, name: &str, template: &T) -> anyhow::Resu Ok(()) } -fn gather_test_suites(job_metrics: &HashMap) -> TestSuites { +fn gather_test_suites(job_metrics: &HashMap) -> TestSuites<'_> { struct CoarseTestSuite<'a> { tests: BTreeMap>, } diff --git a/src/ci/citool/src/utils.rs b/src/ci/citool/src/utils.rs index 0367d349a1ef4..3176cb62f6066 100644 --- a/src/ci/citool/src/utils.rs +++ b/src/ci/citool/src/utils.rs @@ -31,6 +31,6 @@ where } /// Normalizes Windows-style path delimiters to Unix-style paths. -pub fn normalize_path_delimiters(name: &str) -> Cow { +pub fn normalize_path_delimiters(name: &str) -> Cow<'_, str> { if name.contains("\\") { name.replace('\\', "/").into() } else { name.into() } }