From 6fc3880b0402cd31dd02a165c767958e540d88f2 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 1 Apr 2026 08:09:16 +0000 Subject: [PATCH 01/16] hwaddress: automatically add -Ctarget-feature=tagged-globals --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 1 + compiler/rustc_codegen_ssa/src/target_features.rs | 11 ++++++++++- src/doc/unstable-book/src/compiler-flags/sanitizer.md | 9 ++------- tests/codegen-llvm/sanitizer/hwasan-vs-khwasan.rs | 2 ++ tests/ui/sanitizer/hwaddress.rs | 4 +--- tests/ui/sanitizer/hwaddress.stderr | 7 ------- 6 files changed, 16 insertions(+), 18 deletions(-) delete mode 100644 tests/ui/sanitizer/hwaddress.stderr diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 3e0a6efde0252..180559d28d848 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -630,6 +630,7 @@ fn llvm_features_by_flags(sess: &Session, features: &mut Vec) { } target_features::retpoline_features_by_flags(sess, features); + target_features::sanitizer_features_by_flags(sess, features); // -Zfixed-x18 if sess.opts.unstable_opts.fixed_x18 { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 8ac3f0555db27..24f731c01996d 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -10,7 +10,7 @@ use rustc_session::Session; use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, edit_distance, sym}; -use rustc_target::spec::Arch; +use rustc_target::spec::{Arch, SanitizerSet}; use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability}; use smallvec::SmallVec; @@ -460,6 +460,15 @@ pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec) { } } +/// Computes the backend target features to be added to account for sanitizer flags. +pub fn sanitizer_features_by_flags(sess: &Session, features: &mut Vec) { + // It's intentional that this is done only for non-kernel version of hwaddress. This matches + // clang behavior. + if sess.sanitizers().contains(SanitizerSet::HWADDRESS) { + features.push("+tagged-globals".into()); + } +} + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { rust_target_features: |tcx, cnum| { diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index eb070c22dc288..b0f6c97ff5a73 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -552,10 +552,6 @@ HWAddressSanitizer is supported on the following targets: * `aarch64-linux-android` * `aarch64-unknown-linux-gnu` -HWAddressSanitizer requires `tagged-globals` target feature to instrument -globals. To enable this target feature compile with `-C -target-feature=+tagged-globals` - See the [Clang HWAddressSanitizer documentation][clang-hwasan] for more details. ## Example @@ -570,9 +566,8 @@ fn main() { ``` ```shell -$ rustc main.rs -Zsanitizer=hwaddress -C target-feature=+tagged-globals -C -linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=lld --target -aarch64-unknown-linux-gnu +$ rustc main.rs -Zsanitizer=hwaddress -Clinker=aarch64-linux-gnu-gcc +-Clink-arg=-fuse-ld=lld --target aarch64-unknown-linux-gnu ``` ```shell diff --git a/tests/codegen-llvm/sanitizer/hwasan-vs-khwasan.rs b/tests/codegen-llvm/sanitizer/hwasan-vs-khwasan.rs index 93932d86582fa..c34df8c3c5acd 100644 --- a/tests/codegen-llvm/sanitizer/hwasan-vs-khwasan.rs +++ b/tests/codegen-llvm/sanitizer/hwasan-vs-khwasan.rs @@ -18,6 +18,7 @@ extern crate minicore; // hwasan: @__hwasan_tls // hwasan: call void @llvm.hwasan.check.memaccess.shortgranules // hwasan: declare void @__hwasan_init() +// hwasan: attributes #0 {{.*"target-features"=".*\+tagged-globals.*"}} // The `__hwasan_tls` symbol is unconditionally declared by LLVM's `HWAddressSanitizer` pass. // However, in kernel mode KHWASAN does not actually use it (because shadow mapping is fixed @@ -33,6 +34,7 @@ extern crate minicore; // // khwasan-NOT: @__hwasan_init // khwasan: call void @llvm.hwasan.check.memaccess.shortgranules +// khwasan-NOT: attributes #0 {{.*"target-features"=".*\+tagged-globals.*"}} #[no_mangle] pub fn test(b: &mut u8) -> u8 { *b diff --git a/tests/ui/sanitizer/hwaddress.rs b/tests/ui/sanitizer/hwaddress.rs index 8666e7de44924..7557b0f53f7c8 100644 --- a/tests/ui/sanitizer/hwaddress.rs +++ b/tests/ui/sanitizer/hwaddress.rs @@ -1,7 +1,7 @@ //@ needs-sanitizer-support //@ needs-sanitizer-hwaddress // -//@ compile-flags: -Z sanitizer=hwaddress -O -g -C target-feature=+tagged-globals -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -Z sanitizer=hwaddress -O -g -C unsafe-allow-abi-mismatch=sanitizer // //@ run-fail //@ error-pattern: HWAddressSanitizer: tag-mismatch @@ -15,5 +15,3 @@ fn main() { let code = unsafe { *xs.offset(4) }; std::process::exit(code); } - -//~? WARN unknown and unstable feature specified for `-Ctarget-feature`: `tagged-globals` diff --git a/tests/ui/sanitizer/hwaddress.stderr b/tests/ui/sanitizer/hwaddress.stderr deleted file mode 100644 index 37afe0bd779ec..0000000000000 --- a/tests/ui/sanitizer/hwaddress.stderr +++ /dev/null @@ -1,7 +0,0 @@ -warning: unknown and unstable feature specified for `-Ctarget-feature`: `tagged-globals` - | - = note: it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future - = help: consider filing a feature request - -warning: 1 warning emitted - From 221c39a8b0154f559a8fed16f49b633b819bdc74 Mon Sep 17 00:00:00 2001 From: Marco Liebel Date: Fri, 3 Apr 2026 11:04:13 -0700 Subject: [PATCH 02/16] Add myself as co-maintainer for hexagon-unknown-linux-musl Two dedicated target maintainers are needed for tier 2 promotion. Coordinated with the existing maintainer @androm3da. --- src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md index eefe821339060..9f4e8239cc7d2 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md @@ -12,6 +12,7 @@ DSP architecture. ## Target maintainers [@androm3da](https://github.com/androm3da) +[@quic-mliebel](https://github.com/quic-mliebel) ## Requirements The target is cross-compiled. This target supports `std`. By default, code From 690d1938a70074eb8bb7186693d279da056bf37b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 14 Mar 2026 09:30:03 +0100 Subject: [PATCH 03/16] Break a single query cycle in the deadlock handler --- compiler/rustc_interface/src/util.rs | 4 +- compiler/rustc_query_impl/src/job.rs | 91 ++++++++-------------------- compiler/rustc_query_impl/src/lib.rs | 2 +- 3 files changed, 27 insertions(+), 70 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0cd0275f96bbb..24b23cc4199e9 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -184,7 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals< use rustc_data_structures::defer; use rustc_middle::ty::tls; - use rustc_query_impl::break_query_cycles; + use rustc_query_impl::break_query_cycle; let thread_stack_size = init_stack_size(thread_builder_diag); @@ -260,7 +260,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; ) }, ); - break_query_cycles(job_map, ®istry); + break_query_cycle(job_map, ®istry); }) }) }); diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index f8098da593718..2486c0abfde80 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -303,29 +303,17 @@ fn process_cycle<'tcx>(job_map: &QueryJobMap<'tcx>, stack: Vec<(Span, QueryJobId } } -/// Looks for a query cycle using the last query in `jobs`. -/// If a cycle is found, all queries in the cycle is removed from `jobs` and -/// the function return true. -/// If a cycle was not found, the starting query is removed from `jobs` and -/// the function returns false. -fn remove_cycle<'tcx>( +/// Looks for a query cycle starting at `query`. +/// Returns a waiter to resume if a cycle is found. +fn find_and_process_cycle<'tcx>( job_map: &QueryJobMap<'tcx>, - jobs: &mut Vec, - wakelist: &mut Vec>>, -) -> bool { + query: QueryJobId, +) -> Option>> { let mut visited = FxHashSet::default(); let mut stack = Vec::new(); - // Look for a cycle starting with the last query in `jobs` if let ControlFlow::Break(resumable) = - find_cycle(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) + find_cycle(job_map, query, DUMMY_SP, &mut stack, &mut visited) { - // Remove the queries in our cycle from the list of jobs to look at - for r in &stack { - if let Some(pos) = jobs.iter().position(|j| j == &r.1) { - jobs.remove(pos); - } - } - // Create the cycle error let error = process_cycle(job_map, stack); @@ -340,62 +328,31 @@ fn remove_cycle<'tcx>( *waiter.cycle.lock() = Some(error); // Put the waiter on the list of things to resume - wakelist.push(waiter); - - true + Some(waiter) } else { - false + None } } /// Detects query cycles by using depth first search over all active query jobs. /// If a query cycle is found it will break the cycle by finding an edge which /// uses a query latch and then resuming that waiter. -/// There may be multiple cycles involved in a deadlock, so this searches -/// all active queries for cycles before finally resuming all the waiters at once. -pub fn break_query_cycles<'tcx>( - job_map: QueryJobMap<'tcx>, - registry: &rustc_thread_pool::Registry, -) { - let mut wakelist = Vec::new(); - // It is OK per the comments: - // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932 - // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392 - #[allow(rustc::potential_query_instability)] - let mut jobs: Vec = job_map.map.keys().copied().collect(); - - let mut found_cycle = false; - - while jobs.len() > 0 { - if remove_cycle(&job_map, &mut jobs, &mut wakelist) { - found_cycle = true; - } - } - - // Check that a cycle was found. It is possible for a deadlock to occur without - // a query cycle if a query which can be waited on uses Rayon to do multithreading - // internally. Such a query (X) may be executing on 2 threads (A and B) and A may - // wait using Rayon on B. Rayon may then switch to executing another query (Y) - // which in turn will wait on X causing a deadlock. We have a false dependency from - // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here - // only considers the true dependency and won't detect a cycle. - if !found_cycle { - panic!( - "deadlock detected as we're unable to find a query cycle to break\n\ - current query map:\n{job_map:#?}", - ); - } - - // Mark all the thread we're about to wake up as unblocked. This needs to be done before - // we wake the threads up as otherwise Rayon could detect a deadlock if a thread we - // resumed fell asleep and this thread had yet to mark the remaining threads as unblocked. - for _ in 0..wakelist.len() { - rustc_thread_pool::mark_unblocked(registry); - } - - for waiter in wakelist.into_iter() { - waiter.condvar.notify_one(); - } +/// +/// There may be multiple cycles involved in a deadlock, but this only breaks one at a time so +/// there will be multiple rounds through the deadlock handler if multiple cycles are present. +#[allow(rustc::potential_query_instability)] +pub fn break_query_cycle<'tcx>(job_map: QueryJobMap<'tcx>, registry: &rustc_thread_pool::Registry) { + // Look for a cycle starting at each query job + let waiter = job_map + .map + .keys() + .find_map(|query| find_and_process_cycle(&job_map, *query)) + .expect("unable to find a query cycle"); + + // Mark the thread we're about to wake up as unblocked. + rustc_thread_pool::mark_unblocked(registry); + + assert!(waiter.condvar.notify_one(), "unable to wake the waiter"); } pub fn print_query_stack<'tcx>( diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 27bfe1451f64f..d13ecaa2dee5c 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::TyCtxt; pub use crate::dep_kind_vtables::make_dep_kind_vtables; pub use crate::execution::{CollectActiveJobsKind, collect_active_query_jobs}; -pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; +pub use crate::job::{QueryJobMap, break_query_cycle, print_query_stack}; mod dep_kind_vtables; mod error; From f493cab3e3d99692bf4de0d28acab3a918472d2f Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Thu, 9 Apr 2026 09:35:54 +0100 Subject: [PATCH 04/16] Add a test for an LLVM crash "Vector elements must have same size" --- tests/ui/derives/clone-vector-element-size.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/ui/derives/clone-vector-element-size.rs diff --git a/tests/ui/derives/clone-vector-element-size.rs b/tests/ui/derives/clone-vector-element-size.rs new file mode 100644 index 0000000000000..1f29657372452 --- /dev/null +++ b/tests/ui/derives/clone-vector-element-size.rs @@ -0,0 +1,17 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/104037. +//! LLVM used to hit an assertion "Vector elements must have same size" +//! when compiling derived Clone with MIR optimisation level of 3. + +//@ build-pass +//@ compile-flags: -Zmir-opt-level=3 -Copt-level=3 + +#[derive(Clone)] +pub struct Foo(Bar, u32); + +#[derive(Clone, Copy)] +pub struct Bar(u8, u8, u8); + +fn main() { + let foo: Vec = Vec::new(); + let _ = foo.clone(); +} From 12e847f75c57deb188c977a1254ad5252fb620e6 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Thu, 9 Apr 2026 09:35:57 +0100 Subject: [PATCH 05/16] Add a test for a const evaluator ICE on a self-receiver type mismatch --- .../self-receiver-type-mismatch.rs | 24 +++++++++++++++++++ .../self-receiver-type-mismatch.stderr | 12 ++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/ui/traits/const-traits/self-receiver-type-mismatch.rs create mode 100644 tests/ui/traits/const-traits/self-receiver-type-mismatch.stderr diff --git a/tests/ui/traits/const-traits/self-receiver-type-mismatch.rs b/tests/ui/traits/const-traits/self-receiver-type-mismatch.rs new file mode 100644 index 0000000000000..61f0dbe3a9751 --- /dev/null +++ b/tests/ui/traits/const-traits/self-receiver-type-mismatch.rs @@ -0,0 +1,24 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/112623. +//! The const evaluator used to ICE with an assertion failure on a size mismatch +//! when a trait impl changed the `self` receiver type from by-value to by-reference. + +#![feature(const_trait_impl)] + +const trait Func { + fn trigger(self) -> usize; +} + +struct Cls; + +impl const Func for Cls { + fn trigger(&self, a: usize) -> usize { + //~^ ERROR method `trigger` has 2 parameters but the declaration in trait `Func::trigger` has 1 + 0 + } +} + +enum Bug { + V(T), +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/self-receiver-type-mismatch.stderr b/tests/ui/traits/const-traits/self-receiver-type-mismatch.stderr new file mode 100644 index 0000000000000..4fd65d38a40d5 --- /dev/null +++ b/tests/ui/traits/const-traits/self-receiver-type-mismatch.stderr @@ -0,0 +1,12 @@ +error[E0050]: method `trigger` has 2 parameters but the declaration in trait `Func::trigger` has 1 + --> $DIR/self-receiver-type-mismatch.rs:14:16 + | +LL | fn trigger(self) -> usize; + | ---- trait requires 1 parameter +... +LL | fn trigger(&self, a: usize) -> usize { + | ^^^^^^^^^^^^^^^ expected 1 parameter, found 2 + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0050`. From fc9f492049072489988d201dd2baed1b6c513925 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Thu, 9 Apr 2026 09:36:00 +0100 Subject: [PATCH 06/16] Add a codegen test for a missed optimisation with spare niches --- .../enum/enum-array-index-spare-niche.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/codegen-llvm/enum/enum-array-index-spare-niche.rs diff --git a/tests/codegen-llvm/enum/enum-array-index-spare-niche.rs b/tests/codegen-llvm/enum/enum-array-index-spare-niche.rs new file mode 100644 index 0000000000000..e758996a29e06 --- /dev/null +++ b/tests/codegen-llvm/enum/enum-array-index-spare-niche.rs @@ -0,0 +1,28 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/113899. +//! When indexing into an array of an enum type with spare niches, the compiler +//! used to emit a superfluous branch checking whether the loaded value was +//! a niche value. Every element in the array is a valid variant, so this check +//! is unnecessary and should be optimised away. + +//@ compile-flags: -Copt-level=3 +#![crate_type = "lib"] + +#[derive(Clone, Copy)] +pub enum Outer { + A([u8; 8]), + B([u8; 8]), +} + +pub struct Error(u8); + +// CHECK-LABEL: @test +#[no_mangle] +pub fn test(x: usize) -> Result { + // There should be exactly one comparison: the bounds check on `x`. + // There must be no second comparison checking the discriminant + // against the niche value used by `Option` (from `get()`). + // CHECK: icmp ult + // CHECK-NOT: icmp + // CHECK: ret void + [Outer::A([10; 8]), Outer::B([20; 8])].get(x).copied().ok_or(Error(5)) +} From 0fbab04fcf95c04ed30fc3173d5b4511890a90d2 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 10 Mar 2026 16:32:55 +0100 Subject: [PATCH 07/16] minor follow up to removing soft mode `#[unstable]` --- src/tools/lint-docs/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index bc38e931fe515..f7d487333e32b 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -332,7 +332,6 @@ impl<'a> LintExtractor<'a> { if matches!( lint.name.as_str(), "unused_features" // broken lint - | "soft_unstable" // cannot have a stable example ) { return Ok(()); } From 6cd5315f1d3d52ce6370ebb844034fc27bbd4059 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 9 Apr 2026 13:12:24 +0300 Subject: [PATCH 08/16] Implement `GenericTypeVisitable` for some types This is required for rust-analyzer. --- compiler/rustc_type_ir/src/binder.rs | 11 ++-- compiler/rustc_type_ir/src/const_kind.rs | 2 +- compiler/rustc_type_ir/src/generic_visit.rs | 62 ++++----------------- 3 files changed, 20 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 0b0f0fd2f4249..76140e6a762fe 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -956,7 +956,7 @@ pub enum BoundVarIndexKind { /// identified by both a universe, as well as a name residing within that universe. Distinct bound /// regions/types/consts within the same universe simply have an unknown relationship to one #[derive_where(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash; I: Interner, T)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -995,7 +995,7 @@ where } #[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)] -#[derive(Lift_Generic)] +#[derive(Lift_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -1058,7 +1058,7 @@ impl BoundRegionKind { } #[derive_where(Clone, Copy, PartialEq, Eq, Debug, Hash; I: Interner)] -#[derive(Lift_Generic)] +#[derive(Lift_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -1069,7 +1069,7 @@ pub enum BoundTyKind { } #[derive_where(Clone, Copy, PartialEq, Eq, Debug, Hash; I: Interner)] -#[derive(Lift_Generic)] +#[derive(Lift_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -1104,6 +1104,7 @@ impl BoundVariableKind { } #[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, HashStable_NoContext, Decodable_NoContext) @@ -1164,6 +1165,7 @@ impl PlaceholderRegion { } #[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -1229,6 +1231,7 @@ impl PlaceholderType { } #[derive_where(Clone, Copy, PartialEq, Debug, Eq, Hash; I: Interner)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 2877364762f46..68c87dd0bbeb7 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -141,7 +141,7 @@ impl HashStable for InferConst { /// `ValTree` does not have this problem with representation, as it only contains integers or /// lists of (nested) `ty::Const`s (which may indirectly contain more `ValTree`s). #[derive_where(Clone, Copy, Debug, Hash, Eq, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) diff --git a/compiler/rustc_type_ir/src/generic_visit.rs b/compiler/rustc_type_ir/src/generic_visit.rs index ee55c63ab4779..543397ff155b4 100644 --- a/compiler/rustc_type_ir/src/generic_visit.rs +++ b/compiler/rustc_type_ir/src/generic_visit.rs @@ -1,45 +1,13 @@ -//! A visiting traversal mechanism for complex data structures that contain type -//! information. +//! Special visiting used by rust-analyzer only. //! -//! This is a read-only traversal of the data structure. +//! It is different from `TypeVisitable` in two ways: //! -//! This traversal has limited flexibility. Only a small number of "types of -//! interest" within the complex data structures can receive custom -//! visitation. These are the ones containing the most important type-related -//! information, such as `Ty`, `Predicate`, `Region`, and `Const`. -//! -//! There are three traits involved in each traversal. -//! - `GenericTypeVisitable`. This is implemented once for many types, including: -//! - Types of interest, for which the methods delegate to the visitor. -//! - All other types, including generic containers like `Vec` and `Option`. -//! It defines a "skeleton" of how they should be visited. -//! - `TypeSuperVisitable`. This is implemented only for recursive types of -//! interest, and defines the visiting "skeleton" for these types. (This -//! excludes `Region` because it is non-recursive, i.e. it never contains -//! other types of interest.) -//! - `CustomizableTypeVisitor`. This is implemented for each visitor. This defines how -//! types of interest are visited. -//! -//! This means each visit is a mixture of (a) generic visiting operations, and (b) -//! custom visit operations that are specific to the visitor. -//! - The `GenericTypeVisitable` impls handle most of the traversal, and call into -//! `CustomizableTypeVisitor` when they encounter a type of interest. -//! - A `CustomizableTypeVisitor` may call into another `GenericTypeVisitable` impl, because some of -//! the types of interest are recursive and can contain other types of interest. -//! - A `CustomizableTypeVisitor` may also call into a `TypeSuperVisitable` impl, because each -//! visitor might provide custom handling only for some types of interest, or -//! only for some variants of each type of interest, and then use default -//! traversal for the remaining cases. -//! -//! For example, if you have `struct S(Ty, U)` where `S: GenericTypeVisitable` and `U: -//! GenericTypeVisitable`, and an instance `s = S(ty, u)`, it would be visited like so: -//! ```text -//! s.generic_visit_with(visitor) calls -//! - ty.generic_visit_with(visitor) calls -//! - visitor.visit_ty(ty) may call -//! - ty.super_generic_visit_with(visitor) -//! - u.generic_visit_with(visitor) -//! ``` +//! - The visitor is a generic of the trait and not the method, allowing types to attach +//! special behavior to visitors (as long as they know it; we don't use this capability +//! in rustc crates, but rust-analyzer needs it). +//! - It **must visit** every field. This is why we don't have an attribute like `#[type_visitable(ignore)]` +//! for this visit. The reason for this is soundness: rust-analyzer uses this visit to +//! garbage collect types, so a missing field can mean a use after free use std::sync::Arc; @@ -53,16 +21,6 @@ use thin_vec::ThinVec; /// To implement this conveniently, use the derive macro located in /// `rustc_macros`. pub trait GenericTypeVisitable { - /// The entry point for visiting. To visit a value `t` with a visitor `v` - /// call: `t.generic_visit_with(v)`. - /// - /// For most types, this just traverses the value, calling `generic_visit_with` on - /// each field/element. - /// - /// For types of interest (such as `Ty`), the implementation of this method - /// that calls a visitor method specifically for that type (such as - /// `V::visit_ty`). This is where control transfers from `GenericTypeVisitable` to - /// `CustomizableTypeVisitor`. fn generic_visit_with(&self, visitor: &mut V); } @@ -216,6 +174,10 @@ macro_rules! trivial_impls { }; } +impl GenericTypeVisitable for std::marker::PhantomData { + fn generic_visit_with(&self, _visitor: &mut V) {} +} + trivial_impls!( (), rustc_ast_ir::Mutability, From d024e5762f188ac90f79c7b8030315c854666cb5 Mon Sep 17 00:00:00 2001 From: malezjaa Date: Thu, 9 Apr 2026 16:12:51 +0200 Subject: [PATCH 09/16] Fix if branch in op.rs --- compiler/rustc_hir_typeck/src/op.rs | 38 +++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 4eac1fc004cc8..8b08c7b68b784 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -294,24 +294,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.apply_adjustments(lhs_expr, vec![autoref]); } - if let ty::Ref(_, _, mutbl) = method.sig.inputs()[1].kind() { - // Allow two-phase borrows for binops in initial deployment - // since they desugar to methods - let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes); - let autoref = Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), - target: method.sig.inputs()[1], - }; - // HACK(eddyb) Bypass checks due to reborrows being in - // some cases applied on the RHS, on top of which we need - // to autoref, which is not allowed by apply_adjustments. - // self.apply_adjustments(rhs_expr, vec![autoref]); - self.typeck_results - .borrow_mut() - .adjustments_mut() - .entry(rhs_expr.hir_id) - .or_default() - .push(autoref); + if by_ref_binop { + if let ty::Ref(_, _, mutbl) = method.sig.inputs()[1].kind() { + // Allow two-phase borrows for binops in initial deployment + // since they desugar to methods + let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes); + let autoref = Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), + target: method.sig.inputs()[1], + }; + // HACK(eddyb) Bypass checks due to reborrows being in + // some cases applied on the RHS, on top of which we need + // to autoref, which is not allowed by apply_adjustments. + // self.apply_adjustments(rhs_expr, vec![autoref]); + self.typeck_results + .borrow_mut() + .adjustments_mut() + .entry(rhs_expr.hir_id) + .or_default() + .push(autoref); + } } } From dbc08cfd5798ce91930e11da36145f84bfb7ce9c Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:39:46 +0000 Subject: [PATCH 10/16] main-termination --- .../rustc_hir_analysis/src/check/entry.rs | 67 +++++++++---------- .../rustc_hir_analysis/src/check/wfcheck.rs | 3 +- .../main-termination-lifetime-issue-148421.rs | 17 +++++ ...n-termination-lifetime-issue-148421.stderr | 11 +++ 4 files changed, 63 insertions(+), 35 deletions(-) create mode 100644 tests/ui/typeck/main-termination-lifetime-issue-148421.rs create mode 100644 tests/ui/typeck/main-termination-lifetime-issue-148421.stderr diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index a6dae521db884..4c72f5a654e1d 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -7,22 +7,23 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::span_bug; use rustc_middle::ty::{self, TyCtxt, TypingMode}; use rustc_session::config::EntryFnType; -use rustc_span::Span; use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; +use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use super::check_function_signature; use crate::errors; -pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) { +pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { match tcx.entry_fn(()) { Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id), - _ => {} + _ => Ok(()), } } -fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { +fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) -> Result<(), ErrorGuaranteed> { let main_fnsig = tcx.fn_sig(main_def_id).instantiate_identity(); let main_span = tcx.def_span(main_def_id); @@ -87,20 +88,20 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { } } - let mut error = false; let main_diagnostics_def_id = main_fn_diagnostics_def_id(tcx, main_def_id, main_span); let main_asyncness = tcx.asyncness(main_def_id); if main_asyncness.is_async() { let asyncness_span = main_fn_asyncness_span(tcx, main_def_id); - tcx.dcx() - .emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span }); - error = true; + return Err(tcx + .dcx() + .emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span })); } if let Some(attr_span) = find_attr!(tcx, main_def_id, TrackCaller(span) => *span) { - tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span }); - error = true; + return Err(tcx + .dcx() + .emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span })); } if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty() @@ -108,12 +109,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { && !tcx.sess.target.is_like_wasm && !tcx.sess.opts.actually_rustdoc { - tcx.dcx().emit_err(errors::TargetFeatureOnMain { main: main_span }); - error = true; - } - - if error { - return; + return Err(tcx.dcx().emit_err(errors::TargetFeatureOnMain { main: main_span })); } // Main should have no WC, so empty param env is OK here. @@ -123,8 +119,9 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { let return_ty = main_fnsig.output(); let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span); let Some(return_ty) = return_ty.no_bound_vars() else { - tcx.dcx().emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span }); - return; + return Err(tcx + .dcx() + .emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span })); }; let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let cause = traits::ObligationCause::new( @@ -137,8 +134,16 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { ocx.register_bound(cause, param_env, norm_return_ty, term_did); let errors = ocx.evaluate_obligations_error_on_ambiguity(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(errors); - error = true; + return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); + } + + let region_errors = + infcx.resolve_regions(main_diagnostics_def_id, param_env, ty::List::empty()); + + if !region_errors.is_empty() { + return Err(infcx + .err_ctxt() + .report_region_errors(main_diagnostics_def_id, ®ion_errors)); } // now we can take the return type of the given main function expected_return_type = norm_return_ty; @@ -147,10 +152,6 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { expected_return_type = tcx.types.unit; } - if error { - return; - } - let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig( [], expected_return_type, @@ -159,7 +160,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { ExternAbi::Rust, )); - if check_function_signature( + check_function_signature( tcx, ObligationCause::new( main_span, @@ -168,26 +169,24 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { ), main_def_id, expected_sig, - ) - .is_err() - { - return; - } + )?; let main_fn_generics = tcx.generics_of(main_def_id); let main_fn_predicates = tcx.predicates_of(main_def_id); if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() { let generics_param_span = main_fn_generics_params_span(tcx, main_def_id); - tcx.dcx().emit_err(errors::MainFunctionGenericParameters { + return Err(tcx.dcx().emit_err(errors::MainFunctionGenericParameters { span: generics_param_span.unwrap_or(main_span), label_span: generics_param_span, - }); + })); } else if !main_fn_predicates.predicates.is_empty() { // generics may bring in implicit predicates, so we skip this check if generics is present. let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id); - tcx.dcx().emit_err(errors::WhereClauseOnMain { + return Err(tcx.dcx().emit_err(errors::WhereClauseOnMain { span: generics_where_clauses_span.unwrap_or(main_span), generics_span: generics_where_clauses_span, - }); + })); } + + Ok(()) } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 5656c4566d9ff..734154238b1f7 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2350,7 +2350,8 @@ pub(super) fn check_type_wf(tcx: TyCtxt<'_>, (): ()) -> Result<(), ErrorGuarante })) .and(items.par_nested_bodies(|item| tcx.ensure_result().check_well_formed(item))) .and(items.par_opaques(|item| tcx.ensure_result().check_well_formed(item))); - super::entry::check_for_entry_fn(tcx); + + super::entry::check_for_entry_fn(tcx)?; res } diff --git a/tests/ui/typeck/main-termination-lifetime-issue-148421.rs b/tests/ui/typeck/main-termination-lifetime-issue-148421.rs new file mode 100644 index 0000000000000..ce8a36cc2932f --- /dev/null +++ b/tests/ui/typeck/main-termination-lifetime-issue-148421.rs @@ -0,0 +1,17 @@ +// This test checks that the compiler correctly handles lifetime requirements +// on the `Termination` trait for the `main` function return type. +// See https://github.com/rust-lang/rust/issues/148421 + +use std::process::ExitCode; +use std::process::Termination; + +trait IsStatic {} +impl<'a: 'static> IsStatic for &'a () {} + +struct Thing; + +impl Termination for Thing where for<'a> &'a (): IsStatic { + fn report(self) -> ExitCode { panic!() } +} + +fn main() -> Thing { Thing } //~ ERROR implementation of `IsStatic` is not general enough diff --git a/tests/ui/typeck/main-termination-lifetime-issue-148421.stderr b/tests/ui/typeck/main-termination-lifetime-issue-148421.stderr new file mode 100644 index 0000000000000..f0bb77cfe4c8c --- /dev/null +++ b/tests/ui/typeck/main-termination-lifetime-issue-148421.stderr @@ -0,0 +1,11 @@ +error: implementation of `IsStatic` is not general enough + --> $DIR/main-termination-lifetime-issue-148421.rs:17:14 + | +LL | fn main() -> Thing { Thing } + | ^^^^^ implementation of `IsStatic` is not general enough + | + = note: `IsStatic` would have to be implemented for the type `&'0 ()`, for any lifetime `'0`... + = note: ...but `IsStatic` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1` + +error: aborting due to 1 previous error + From b65574d78220ab70da315964224bb73c21de1b46 Mon Sep 17 00:00:00 2001 From: Fayti1703 Date: Fri, 10 Apr 2026 00:21:53 +0200 Subject: [PATCH 11/16] Add (failing) ui test for denying global_allocator + thread_local --- tests/ui/allocator/no-thread-local.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/ui/allocator/no-thread-local.rs diff --git a/tests/ui/allocator/no-thread-local.rs b/tests/ui/allocator/no-thread-local.rs new file mode 100644 index 0000000000000..33c433d5c988a --- /dev/null +++ b/tests/ui/allocator/no-thread-local.rs @@ -0,0 +1,10 @@ +#![feature(thread_local)] + +use std::alloc::System; + +#[global_allocator] +#[thread_local] +//~^ ERROR: allocators cannot be `#[thread_local]` +static A: System = System; + +fn main() {} From 95c1a3793e8d348205cab69962da709c6c63d8a4 Mon Sep 17 00:00:00 2001 From: Fayti1703 Date: Fri, 10 Apr 2026 00:33:27 +0200 Subject: [PATCH 12/16] Deny global_allocator + thread_local --- compiler/rustc_builtin_macros/src/errors.rs | 7 +++++++ compiler/rustc_builtin_macros/src/global_allocator.rs | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 87f1ff960a821..72fe9e7843bb9 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -158,6 +158,13 @@ pub(crate) struct AllocMustStatics { pub(crate) span: Span, } +#[derive(Diagnostic)] +#[diag("allocators cannot be `#[thread_local]`")] +pub(crate) struct AllocCannotThreadLocal { + #[primary_span] + pub(crate) span: Span, +} + pub(crate) use autodiff::*; mod autodiff { diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 85ad8a63a54f0..f8a1607778e85 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -38,6 +38,12 @@ pub(crate) fn expand( return vec![orig_item]; }; + // Forbid `#[thread_local]` attributes on the item + if let Some(_) = item.attrs.iter().find(|x| { x.has_name(sym::thread_local) }) { + ecx.dcx().emit_err(errors::AllocCannotThreadLocal { span: item.span_with_attributes() }); + return vec![orig_item]; + } + // Generate a bunch of new items using the AllocFnFactory let span = ecx.with_def_site_ctxt(item.span); let f = AllocFnFactory { span, ty_span, global: ident, cx: ecx }; From 9614f285a70be5b310029a30b38b6fb9adfac80c Mon Sep 17 00:00:00 2001 From: Fayti1703 Date: Fri, 10 Apr 2026 00:33:44 +0200 Subject: [PATCH 13/16] Update ui test for deny global_allocator + thread_local --- tests/ui/allocator/no-thread-local.stderr | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/ui/allocator/no-thread-local.stderr diff --git a/tests/ui/allocator/no-thread-local.stderr b/tests/ui/allocator/no-thread-local.stderr new file mode 100644 index 0000000000000..27aa8fd4a7fa9 --- /dev/null +++ b/tests/ui/allocator/no-thread-local.stderr @@ -0,0 +1,10 @@ +error: allocators cannot be `#[thread_local]` + --> $DIR/no-thread-local.rs:6:1 + | +LL | / #[thread_local] +LL | | +LL | | static A: System = System; + | |__________________________^ + +error: aborting due to 1 previous error + From dbe7a4e13ad2c2e0cdaddde1348f8777d1de71ee Mon Sep 17 00:00:00 2001 From: Fayti1703 Date: Fri, 10 Apr 2026 00:45:37 +0200 Subject: [PATCH 14/16] Improve diagnostic for global_allocator + thread_local --- compiler/rustc_builtin_macros/src/errors.rs | 3 +++ .../rustc_builtin_macros/src/global_allocator.rs | 7 +++++-- tests/ui/allocator/no-thread-local.rs | 2 +- tests/ui/allocator/no-thread-local.stderr | 13 ++++++++----- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 72fe9e7843bb9..b5ac84337465a 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -163,6 +163,9 @@ pub(crate) struct AllocMustStatics { pub(crate) struct AllocCannotThreadLocal { #[primary_span] pub(crate) span: Span, + #[label("marked `#[thread_local]` here")] + #[suggestion("remove this attribute", code = "", applicability = "maybe-incorrect")] + pub(crate) attr: Span, } pub(crate) use autodiff::*; diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index f8a1607778e85..f6e28bd780047 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -39,8 +39,11 @@ pub(crate) fn expand( }; // Forbid `#[thread_local]` attributes on the item - if let Some(_) = item.attrs.iter().find(|x| { x.has_name(sym::thread_local) }) { - ecx.dcx().emit_err(errors::AllocCannotThreadLocal { span: item.span_with_attributes() }); + if let Some(attr) = item.attrs.iter().find(|x| { x.has_name(sym::thread_local) }) { + ecx.dcx().emit_err(errors::AllocCannotThreadLocal { + span: item.span, + attr: attr.span + }); return vec![orig_item]; } diff --git a/tests/ui/allocator/no-thread-local.rs b/tests/ui/allocator/no-thread-local.rs index 33c433d5c988a..4548b00816fd2 100644 --- a/tests/ui/allocator/no-thread-local.rs +++ b/tests/ui/allocator/no-thread-local.rs @@ -4,7 +4,7 @@ use std::alloc::System; #[global_allocator] #[thread_local] -//~^ ERROR: allocators cannot be `#[thread_local]` static A: System = System; +//~^ ERROR: allocators cannot be `#[thread_local]` fn main() {} diff --git a/tests/ui/allocator/no-thread-local.stderr b/tests/ui/allocator/no-thread-local.stderr index 27aa8fd4a7fa9..b045dae09c8ef 100644 --- a/tests/ui/allocator/no-thread-local.stderr +++ b/tests/ui/allocator/no-thread-local.stderr @@ -1,10 +1,13 @@ error: allocators cannot be `#[thread_local]` - --> $DIR/no-thread-local.rs:6:1 + --> $DIR/no-thread-local.rs:7:1 | -LL | / #[thread_local] -LL | | -LL | | static A: System = System; - | |__________________________^ +LL | #[thread_local] + | --------------- + | | + | marked `#[thread_local]` here + | help: remove this attribute +LL | static A: System = System; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error From b537c45ec84373293329b2b7d59054f058c5f2b1 Mon Sep 17 00:00:00 2001 From: Fayti1703 Date: Fri, 10 Apr 2026 00:49:17 +0200 Subject: [PATCH 15/16] Apply suggestions from fmt output --- compiler/rustc_builtin_macros/src/global_allocator.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index f6e28bd780047..208562b8d0bc0 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -39,11 +39,8 @@ pub(crate) fn expand( }; // Forbid `#[thread_local]` attributes on the item - if let Some(attr) = item.attrs.iter().find(|x| { x.has_name(sym::thread_local) }) { - ecx.dcx().emit_err(errors::AllocCannotThreadLocal { - span: item.span, - attr: attr.span - }); + if let Some(attr) = item.attrs.iter().find(|x| x.has_name(sym::thread_local)) { + ecx.dcx().emit_err(errors::AllocCannotThreadLocal { span: item.span, attr: attr.span }); return vec![orig_item]; } From a2255aa017f0900efc2aa8c96110aa16d8a56cab Mon Sep 17 00:00:00 2001 From: Paul Kirth Date: Thu, 9 Apr 2026 16:01:10 -0700 Subject: [PATCH 16/16] Set the Fuchsia ABI to the documented minimum Fuchsia only supports the RVA22 + vector as its minimum ABI for RISC-V. https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0234_riscv_abi_rva22+vector --- .../rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs index 3944c4cd59c7f..c15b23b9192ce 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs @@ -6,7 +6,7 @@ pub(crate) fn target() -> Target { let mut base = base::fuchsia::opts(); base.code_model = Some(CodeModel::Medium); base.cpu = "generic-rv64".into(); - base.features = "+m,+a,+f,+d,+c,+zicsr,+zifencei".into(); + base.features = "+m,+a,+f,+d,+c,+v,+zicsr,+zifencei".into(); base.llvm_abiname = LlvmAbi::Lp64d; base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline;