From 835975a12f82707097d8abdadb529c704a2847ac Mon Sep 17 00:00:00 2001 From: Ricky Date: Sun, 22 Feb 2026 22:32:31 +0800 Subject: [PATCH 01/27] alloc: make VecDeque partial equality symmetric with vec/slice/array --- .../alloc/src/collections/vec_deque/macros.rs | 20 ++++++++++++++++++ .../alloc/src/collections/vec_deque/mod.rs | 7 +++++++ library/alloctests/tests/vec_deque.rs | 21 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/library/alloc/src/collections/vec_deque/macros.rs b/library/alloc/src/collections/vec_deque/macros.rs index 5c7913073fe87..4ef2a89e06018 100644 --- a/library/alloc/src/collections/vec_deque/macros.rs +++ b/library/alloc/src/collections/vec_deque/macros.rs @@ -17,3 +17,23 @@ macro_rules! __impl_slice_eq1 { } } } + +macro_rules! __impl_slice_eq2 { + ([$($vars:tt)*] $lhs:ty, $rhs:ty, $($constraints:tt)*) => { + #[stable(feature = "vec_deque_partial_eq_slice", since = "1.17.0")] + impl PartialEq<$rhs> for $lhs + where + T: PartialEq, + $($constraints)* + { + fn eq(&self, other: &$rhs) -> bool { + if self.len() != other.len() { + return false; + } + let (oa, ob) = other.as_slices(); + let (sa, sb) = self[..].split_at(oa.len()); + sa == oa && sb == ob + } + } + } +} diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 9095fc0d4abf4..ab81684ad7467 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -3836,6 +3836,13 @@ __impl_slice_eq1! { [const N: usize] VecDeque, [U; N], } __impl_slice_eq1! { [const N: usize] VecDeque, &[U; N], } __impl_slice_eq1! { [const N: usize] VecDeque, &mut [U; N], } +__impl_slice_eq2! { [] Vec, VecDeque, } +__impl_slice_eq2! { [] &[T], VecDeque, } +__impl_slice_eq2! { [] &mut [T], VecDeque, } +__impl_slice_eq2! { [const N: usize] [T; N], VecDeque, } +__impl_slice_eq2! { [const N: usize] &[T; N], VecDeque, } +__impl_slice_eq2! { [const N: usize] &mut [T; N], VecDeque, } + #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for VecDeque { fn partial_cmp(&self, other: &Self) -> Option { diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index 15cc156d6988f..353d3e64b7b4f 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -631,6 +631,27 @@ fn test_partial_eq_array() { assert!(d == ['a', 'b']); } +#[test] +fn test_partial_eq_vecdeque_reverse() { + let mut d = VecDeque::with_capacity(4); + d.push_back(1); + d.push_back(2); + d.push_back(3); + d.pop_front(); + d.push_back(4); + + let v = vec![2, 3, 4]; + let a = [2, 3, 4]; + let mut b = [2, 3, 4]; + + assert!(v == d); + assert!(&v[..] == d); + assert!(&mut b[..] == d); + assert!(a == d); + assert!(&a == d); + assert!(&mut b == d); +} + #[test] fn test_hash() { let mut x = VecDeque::new(); From b50d79a556e0aa9cd3d738ca60da826e185fe6c7 Mon Sep 17 00:00:00 2001 From: Ricky Date: Mon, 23 Feb 2026 09:12:57 +0800 Subject: [PATCH 02/27] resolve too_generic_eval_ice stderr conflict --- tests/ui/consts/too_generic_eval_ice.current.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/consts/too_generic_eval_ice.current.stderr b/tests/ui/consts/too_generic_eval_ice.current.stderr index 061945e344ede..dd5c3bd51bc52 100644 --- a/tests/ui/consts/too_generic_eval_ice.current.stderr +++ b/tests/ui/consts/too_generic_eval_ice.current.stderr @@ -30,15 +30,15 @@ LL | [5; Self::HOST_SIZE] == [6; 0] | = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; Self::HOST_SIZE]` = help: the following other types implement trait `PartialEq`: + `&[T; N]` implements `PartialEq>` `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq>` + `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq<[U; N]>` `&[u8; N]` implements `PartialEq` `&[u8; N]` implements `PartialEq` `&[u8]` implements `PartialEq` - `&[u8]` implements `PartialEq` - `&mut [T]` implements `PartialEq>` - and 13 others + and 18 others error: aborting due to 4 previous errors From 8cdd010c4e19fb5153247fc9c26a9cb867d376d5 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 25 Aug 2026 10:20:10 +0800 Subject: [PATCH 03/27] update assert-ne-no-invalid-help-issue-146204.stderr for VecDeque PartialEq output --- .../assert-ne-no-invalid-help-issue-146204.stderr | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/macros/assert-ne-no-invalid-help-issue-146204.stderr b/tests/ui/macros/assert-ne-no-invalid-help-issue-146204.stderr index 64e40ebdd7bdf..726505f0bb0cd 100644 --- a/tests/ui/macros/assert-ne-no-invalid-help-issue-146204.stderr +++ b/tests/ui/macros/assert-ne-no-invalid-help-issue-146204.stderr @@ -6,15 +6,15 @@ LL | assert_ne!(buf, b"----"); | = help: the trait `PartialEq<&[u8; 4]>` is not implemented for `[u8; 4]` = help: the following other types implement trait `PartialEq`: + `&[T; N]` implements `PartialEq>` `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq>` + `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq<[U; N]>` `&[u8; N]` implements `PartialEq` `&[u8; N]` implements `PartialEq` `&[u8]` implements `PartialEq` - `&[u8]` implements `PartialEq` - `&mut [T]` implements `PartialEq>` - and 13 others + and 18 others error[E0277]: can't compare `[u8; 4]` with `&[u8; 4]` --> $DIR/assert-ne-no-invalid-help-issue-146204.rs:19:5 @@ -24,15 +24,15 @@ LL | assert_eq!(buf, b"----"); | = help: the trait `PartialEq<&[u8; 4]>` is not implemented for `[u8; 4]` = help: the following other types implement trait `PartialEq`: + `&[T; N]` implements `PartialEq>` `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq>` + `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq<[U; N]>` `&[u8; N]` implements `PartialEq` `&[u8; N]` implements `PartialEq` `&[u8]` implements `PartialEq` - `&[u8]` implements `PartialEq` - `&mut [T]` implements `PartialEq>` - and 13 others + and 18 others error[E0277]: can't compare `[u8; 4]` with `&[u8; 4]` --> $DIR/assert-ne-no-invalid-help-issue-146204.rs:5:30 From 3d3f2f823fa54902104dc5ee13c15915cd89a0ce Mon Sep 17 00:00:00 2001 From: Ricky Date: Fri, 18 Sep 2026 20:35:21 +0800 Subject: [PATCH 04/27] update VecDeque PartialEq stability metadata --- library/alloc/src/collections/vec_deque/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/collections/vec_deque/macros.rs b/library/alloc/src/collections/vec_deque/macros.rs index 4ef2a89e06018..d808a9d8fa993 100644 --- a/library/alloc/src/collections/vec_deque/macros.rs +++ b/library/alloc/src/collections/vec_deque/macros.rs @@ -20,7 +20,7 @@ macro_rules! __impl_slice_eq1 { macro_rules! __impl_slice_eq2 { ([$($vars:tt)*] $lhs:ty, $rhs:ty, $($constraints:tt)*) => { - #[stable(feature = "vec_deque_partial_eq_slice", since = "1.17.0")] + #[stable(feature = "vec_deque_partial_eq_slice", since = "CURRENT_RUSTC_VERSION")] impl PartialEq<$rhs> for $lhs where T: PartialEq, From 9942d36bf6e6c6e5b5d4f34ded101dcb2b2cd9f7 Mon Sep 17 00:00:00 2001 From: Ricky Date: Sat, 19 Sep 2026 16:52:43 +0800 Subject: [PATCH 05/27] fix stability attributes for VecDeque PartialEq impls --- library/alloc/src/collections/vec_deque/macros.rs | 4 ++-- library/alloc/src/collections/vec_deque/mod.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/macros.rs b/library/alloc/src/collections/vec_deque/macros.rs index d808a9d8fa993..dbc1fa3f11578 100644 --- a/library/alloc/src/collections/vec_deque/macros.rs +++ b/library/alloc/src/collections/vec_deque/macros.rs @@ -19,8 +19,8 @@ macro_rules! __impl_slice_eq1 { } macro_rules! __impl_slice_eq2 { - ([$($vars:tt)*] $lhs:ty, $rhs:ty, $($constraints:tt)*) => { - #[stable(feature = "vec_deque_partial_eq_slice", since = "CURRENT_RUSTC_VERSION")] + ($(#[$stability:meta])+, [$($vars:tt)*] $lhs:ty, $rhs:ty, $($constraints:tt)*) => { + $(#[$stability])+ impl PartialEq<$rhs> for $lhs where T: PartialEq, diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index ab81684ad7467..330704c2e7358 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -3836,12 +3836,12 @@ __impl_slice_eq1! { [const N: usize] VecDeque, [U; N], } __impl_slice_eq1! { [const N: usize] VecDeque, &[U; N], } __impl_slice_eq1! { [const N: usize] VecDeque, &mut [U; N], } -__impl_slice_eq2! { [] Vec, VecDeque, } -__impl_slice_eq2! { [] &[T], VecDeque, } -__impl_slice_eq2! { [] &mut [T], VecDeque, } -__impl_slice_eq2! { [const N: usize] [T; N], VecDeque, } -__impl_slice_eq2! { [const N: usize] &[T; N], VecDeque, } -__impl_slice_eq2! { [const N: usize] &mut [T; N], VecDeque, } +__impl_slice_eq2! { #[stable(feature = "slice_partial_eq_vec_deque", since = "CURRENT_RUSTC_VERSION")], [] Vec, VecDeque, } +__impl_slice_eq2! { #[stable(feature = "slice_partial_eq_vec_deque", since = "CURRENT_RUSTC_VERSION")], [] &[T], VecDeque, } +__impl_slice_eq2! { #[stable(feature = "slice_partial_eq_vec_deque", since = "CURRENT_RUSTC_VERSION")], [] &mut [T], VecDeque, } +__impl_slice_eq2! { #[stable(feature = "slice_partial_eq_vec_deque", since = "CURRENT_RUSTC_VERSION")], [const N: usize] [T; N], VecDeque, } +__impl_slice_eq2! { #[stable(feature = "slice_partial_eq_vec_deque", since = "CURRENT_RUSTC_VERSION")], [const N: usize] &[T; N], VecDeque, } +__impl_slice_eq2! { #[stable(feature = "slice_partial_eq_vec_deque", since = "CURRENT_RUSTC_VERSION")], [const N: usize] &mut [T; N], VecDeque, } #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for VecDeque { From 73bafeb8ec3b68f70b798bfbc7b4404bd058f9de Mon Sep 17 00:00:00 2001 From: Yukang Date: Sun, 13 Sep 2026 20:46:47 +0800 Subject: [PATCH 06/27] Add regression tests for import module typo suggestions --- ...d-module-typo-inaccessible-issue-131366.rs | 19 +++++++++ ...dule-typo-inaccessible-issue-131366.stderr | 15 +++++++ .../nested-module-typo-issue-131366.rs | 35 +++++++++++++++++ .../nested-module-typo-issue-131366.stderr | 39 +++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 tests/ui/imports/nested-module-typo-inaccessible-issue-131366.rs create mode 100644 tests/ui/imports/nested-module-typo-inaccessible-issue-131366.stderr create mode 100644 tests/ui/imports/nested-module-typo-issue-131366.rs create mode 100644 tests/ui/imports/nested-module-typo-issue-131366.stderr diff --git a/tests/ui/imports/nested-module-typo-inaccessible-issue-131366.rs b/tests/ui/imports/nested-module-typo-inaccessible-issue-131366.rs new file mode 100644 index 0000000000000..39dc8cd381268 --- /dev/null +++ b/tests/ui/imports/nested-module-typo-inaccessible-issue-131366.rs @@ -0,0 +1,19 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/131366. +//! Similar spelling does not make a private module or a non-module importable. + +//@ edition: 2021 +#![allow(unused_imports, dead_code)] + +mod private { + mod collections { pub struct Item; } +} +use private::collection::Item; +//~^ ERROR unresolved import `private::collection` + +mod non_module { + pub struct Collections; +} +use non_module::Collection::Item; +//~^ ERROR unresolved import `non_module::Collection` + +fn main() {} diff --git a/tests/ui/imports/nested-module-typo-inaccessible-issue-131366.stderr b/tests/ui/imports/nested-module-typo-inaccessible-issue-131366.stderr new file mode 100644 index 0000000000000..020711207ef38 --- /dev/null +++ b/tests/ui/imports/nested-module-typo-inaccessible-issue-131366.stderr @@ -0,0 +1,15 @@ +error[E0432]: unresolved import `private::collection` + --> $DIR/nested-module-typo-inaccessible-issue-131366.rs:10:14 + | +LL | use private::collection::Item; + | ^^^^^^^^^^ could not find `collection` in `private` + +error[E0432]: unresolved import `non_module::Collection` + --> $DIR/nested-module-typo-inaccessible-issue-131366.rs:16:17 + | +LL | use non_module::Collection::Item; + | ^^^^^^^^^^ could not find `Collection` in `non_module` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/nested-module-typo-issue-131366.rs b/tests/ui/imports/nested-module-typo-issue-131366.rs new file mode 100644 index 0000000000000..ff16cd129d0ff --- /dev/null +++ b/tests/ui/imports/nested-module-typo-issue-131366.rs @@ -0,0 +1,35 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/131366. +//! Suggest accessible, similarly named modules within the resolved import prefix. + +//@ edition: 2021 +#![allow(unused_imports, dead_code)] + +use std::collection::HashMap; +//~^ ERROR unresolved import `std::collection` + +mod local { + pub mod collections { + pub struct Item; + } + pub use collections as containers; + pub enum Choices { First } +} + +use local::collection::Item; +//~^ ERROR unresolved import `local::collection` +use local::container::Item as Alias; +//~^ ERROR unresolved import `local::container` +use local::Choice::First; +//~^ ERROR unresolved import `local::Choice` + +mod root_module {} +use crate::root_modul::*; +//~^ ERROR unresolved import `crate::root_modul` + +macro_rules! import { + ($module:ident) => { use local::$module::Item as MacroItem; }; +} +import!(collection); +//~^ ERROR unresolved import `local::collection` + +fn main() {} diff --git a/tests/ui/imports/nested-module-typo-issue-131366.stderr b/tests/ui/imports/nested-module-typo-issue-131366.stderr new file mode 100644 index 0000000000000..3666417c4107c --- /dev/null +++ b/tests/ui/imports/nested-module-typo-issue-131366.stderr @@ -0,0 +1,39 @@ +error[E0432]: unresolved import `std::collection` + --> $DIR/nested-module-typo-issue-131366.rs:7:10 + | +LL | use std::collection::HashMap; + | ^^^^^^^^^^ could not find `collection` in `std` + +error[E0432]: unresolved import `local::collection` + --> $DIR/nested-module-typo-issue-131366.rs:18:12 + | +LL | use local::collection::Item; + | ^^^^^^^^^^ could not find `collection` in `local` + +error[E0432]: unresolved import `local::container` + --> $DIR/nested-module-typo-issue-131366.rs:20:12 + | +LL | use local::container::Item as Alias; + | ^^^^^^^^^ could not find `container` in `local` + +error[E0432]: unresolved import `local::Choice` + --> $DIR/nested-module-typo-issue-131366.rs:22:12 + | +LL | use local::Choice::First; + | ^^^^^^ could not find `Choice` in `local` + +error[E0432]: unresolved import `crate::root_modul` + --> $DIR/nested-module-typo-issue-131366.rs:26:12 + | +LL | use crate::root_modul::*; + | ^^^^^^^^^^ could not find `root_modul` in the crate root + +error[E0432]: unresolved import `local::collection` + --> $DIR/nested-module-typo-issue-131366.rs:32:9 + | +LL | import!(collection); + | ^^^^^^^^^^ could not find `collection` in `local` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0432`. From 02db8ad6411e0f1eaa920ed7d84721be2f058e96 Mon Sep 17 00:00:00 2001 From: Yukang Date: Sun, 13 Sep 2026 20:59:40 +0800 Subject: [PATCH 07/27] Suggest similarly named modules in import paths --- .../rustc_resolve/src/diagnostics/impls.rs | 44 +++++++++++- .../nested-module-typo-issue-131366.fixed | 36 ++++++++++ .../nested-module-typo-issue-131366.rs | 1 + .../nested-module-typo-issue-131366.stderr | 42 +++++++++-- tests/ui/unresolved/unresolved-import.fixed | 70 +++++++++++++++++++ tests/ui/unresolved/unresolved-import.rs | 12 ++-- tests/ui/unresolved/unresolved-import.stderr | 24 +++---- 7 files changed, 206 insertions(+), 23 deletions(-) create mode 100644 tests/ui/imports/nested-module-typo-issue-131366.fixed create mode 100644 tests/ui/unresolved/unresolved-import.fixed diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 6ddfff9f88d83..ee13092abd68e 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -3024,6 +3024,41 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let message = format!("cannot find `{ident}` in {scope}"); + // we may have typo in the middle part of path, try find a candidate with a similar name + // and then check whether it contains a candidate that is accessible from the current scope + let typo_suggestion = if opt_ns.is_none() + && ignore_import.is_some() + && let Some(ModuleOrUniformRoot::Module(module)) = module + && let Some(candidate) = self.early_lookup_typo_candidate( + ScopeSet::Module(TypeNS, module), + parent_scope, + ident, + &|res| matches!(res, Res::Def(DefKind::Mod | DefKind::Enum, _)), + ) + && let Ok(binding) = self.cm().resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + Ident::new(candidate.candidate, ident.span), + TypeNS, + parent_scope, + None, + ignore_decl, + ignore_import, + ) + && self.is_accessible_from(binding.vis(), parent_scope.module) + { + Some(( + vec![(ident.span, Ident::new(candidate.candidate, ident.span).to_string())], + format!( + "{} {} with a similar name exists", + candidate.res.article(), + candidate.res.descr(), + ), + Applicability::MaybeIncorrect, + )) + } else { + None + }; + if module_def_id == Some(CRATE_DEF_ID.to_def_id()) { let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); @@ -3050,6 +3085,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { )), None, ) + } else if let Some(suggestion) = typo_suggestion { + ( + message, + format!("could not find `{ident}` in the crate root"), + Some(suggestion), + None, + ) } else if ident.name == sym::core { ( message, @@ -3182,7 +3224,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ); }; } - (message, msg, None, None) + (message, msg, typo_suggestion, None) } else if ident.name == kw::SelfUpper { // As mentioned above, `opt_ns` being `None` indicates a module path in import. // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an diff --git a/tests/ui/imports/nested-module-typo-issue-131366.fixed b/tests/ui/imports/nested-module-typo-issue-131366.fixed new file mode 100644 index 0000000000000..a84119fe93a8b --- /dev/null +++ b/tests/ui/imports/nested-module-typo-issue-131366.fixed @@ -0,0 +1,36 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/131366. +//! Suggest accessible, similarly named modules within the resolved import prefix. + +//@ edition: 2021 +//@ run-rustfix +#![allow(unused_imports, dead_code)] + +use std::collections::HashMap; +//~^ ERROR unresolved import `std::collection` + +mod local { + pub mod collections { + pub struct Item; + } + pub use collections as containers; + pub enum Choices { First } +} + +use local::collections::Item; +//~^ ERROR unresolved import `local::collection` +use local::containers::Item as Alias; +//~^ ERROR unresolved import `local::container` +use local::Choices::First; +//~^ ERROR unresolved import `local::Choice` + +mod root_module {} +use crate::root_module::*; +//~^ ERROR unresolved import `crate::root_modul` + +macro_rules! import { + ($module:ident) => { use local::$module::Item as MacroItem; }; +} +import!(collections); +//~^ ERROR unresolved import `local::collection` + +fn main() {} diff --git a/tests/ui/imports/nested-module-typo-issue-131366.rs b/tests/ui/imports/nested-module-typo-issue-131366.rs index ff16cd129d0ff..2f269da41b209 100644 --- a/tests/ui/imports/nested-module-typo-issue-131366.rs +++ b/tests/ui/imports/nested-module-typo-issue-131366.rs @@ -2,6 +2,7 @@ //! Suggest accessible, similarly named modules within the resolved import prefix. //@ edition: 2021 +//@ run-rustfix #![allow(unused_imports, dead_code)] use std::collection::HashMap; diff --git a/tests/ui/imports/nested-module-typo-issue-131366.stderr b/tests/ui/imports/nested-module-typo-issue-131366.stderr index 3666417c4107c..922b9219a7854 100644 --- a/tests/ui/imports/nested-module-typo-issue-131366.stderr +++ b/tests/ui/imports/nested-module-typo-issue-131366.stderr @@ -1,38 +1,68 @@ error[E0432]: unresolved import `std::collection` - --> $DIR/nested-module-typo-issue-131366.rs:7:10 + --> $DIR/nested-module-typo-issue-131366.rs:8:10 | LL | use std::collection::HashMap; | ^^^^^^^^^^ could not find `collection` in `std` + | +help: a module with a similar name exists + | +LL | use std::collections::HashMap; + | + error[E0432]: unresolved import `local::collection` - --> $DIR/nested-module-typo-issue-131366.rs:18:12 + --> $DIR/nested-module-typo-issue-131366.rs:19:12 | LL | use local::collection::Item; | ^^^^^^^^^^ could not find `collection` in `local` + | +help: a module with a similar name exists + | +LL | use local::collections::Item; + | + error[E0432]: unresolved import `local::container` - --> $DIR/nested-module-typo-issue-131366.rs:20:12 + --> $DIR/nested-module-typo-issue-131366.rs:21:12 | LL | use local::container::Item as Alias; | ^^^^^^^^^ could not find `container` in `local` + | +help: a module with a similar name exists + | +LL | use local::containers::Item as Alias; + | + error[E0432]: unresolved import `local::Choice` - --> $DIR/nested-module-typo-issue-131366.rs:22:12 + --> $DIR/nested-module-typo-issue-131366.rs:23:12 | LL | use local::Choice::First; | ^^^^^^ could not find `Choice` in `local` + | +help: an enum with a similar name exists + | +LL | use local::Choices::First; + | + error[E0432]: unresolved import `crate::root_modul` - --> $DIR/nested-module-typo-issue-131366.rs:26:12 + --> $DIR/nested-module-typo-issue-131366.rs:27:12 | LL | use crate::root_modul::*; | ^^^^^^^^^^ could not find `root_modul` in the crate root + | +help: a module with a similar name exists + | +LL | use crate::root_module::*; + | + error[E0432]: unresolved import `local::collection` - --> $DIR/nested-module-typo-issue-131366.rs:32:9 + --> $DIR/nested-module-typo-issue-131366.rs:33:9 | LL | import!(collection); | ^^^^^^^^^^ could not find `collection` in `local` + | +help: a module with a similar name exists + | +LL | import!(collections); + | + error: aborting due to 6 previous errors diff --git a/tests/ui/unresolved/unresolved-import.fixed b/tests/ui/unresolved/unresolved-import.fixed new file mode 100644 index 0000000000000..2132e5e21859f --- /dev/null +++ b/tests/ui/unresolved/unresolved-import.fixed @@ -0,0 +1,70 @@ +//@ edition:2015 +//@ run-rustfix + +#![allow(unused_imports, dead_code)] + +use food::beans; +//~^ ERROR unresolved import `foo` [E0432] +//~| NOTE could not find `foo` in the crate root +//~| HELP a module with a similar name exists +//~| SUGGESTION food + +use bar::Bar as x; +//~^ ERROR unresolved import `bar::Baz` [E0432] +//~| NOTE no `Baz` in `bar` +//~| HELP a similar name exists in the module +//~| SUGGESTION Bar + +use food::bag; +//~^ ERROR unresolved import `food::baz` +//~| NOTE no `baz` in `food` +//~| HELP a similar name exists in the module +//~| SUGGESTION bag + +use food::{beans as Foo}; +//~^ ERROR unresolved import `food::beens` [E0432] +//~| NOTE no `beens` in `food` +//~| HELP a similar name exists in the module +//~| SUGGESTION beans + +mod bar { + pub struct Bar; +} + +mod food { + pub use self::zug::baz::{self as bag, Foobar as beans}; + + mod zug { + pub mod baz { + //~^ NOTE module `food::zug::baz` exists but is inaccessible + //~| NOTE not accessible + pub struct Foobar; + } + } +} + +mod m { + enum MyEnum { + MyVariant + } + + use self::MyEnum::*; + //~^ ERROR unresolved import `MyEnum` [E0432] + //~| HELP a similar path exists + //~| SUGGESTION self::MyEnum +} + +mod items { + enum Enum { + Variant + } + + use self::Enum::*; + //~^ ERROR unresolved import `Enum` [E0432] + //~| HELP a similar path exists + //~| SUGGESTION self::Enum + + fn item() {} +} + +fn main() {} diff --git a/tests/ui/unresolved/unresolved-import.rs b/tests/ui/unresolved/unresolved-import.rs index 8795a5342b2fd..ce9bf73537492 100644 --- a/tests/ui/unresolved/unresolved-import.rs +++ b/tests/ui/unresolved/unresolved-import.rs @@ -1,9 +1,13 @@ //@ edition:2015 -use foo::bar; +//@ run-rustfix + +#![allow(unused_imports, dead_code)] + +use foo::beans; //~^ ERROR unresolved import `foo` [E0432] -//~| NOTE use of unresolved module or unlinked crate `foo` -//~| HELP you might be missing a crate named `foo` -//~| SUGGESTION extern crate foo; +//~| NOTE could not find `foo` in the crate root +//~| HELP a module with a similar name exists +//~| SUGGESTION food use bar::Baz as x; //~^ ERROR unresolved import `bar::Baz` [E0432] diff --git a/tests/ui/unresolved/unresolved-import.stderr b/tests/ui/unresolved/unresolved-import.stderr index a5704787688a9..7aac9dc683349 100644 --- a/tests/ui/unresolved/unresolved-import.stderr +++ b/tests/ui/unresolved/unresolved-import.stderr @@ -1,16 +1,16 @@ error[E0432]: unresolved import `foo` - --> $DIR/unresolved-import.rs:2:5 + --> $DIR/unresolved-import.rs:6:5 | -LL | use foo::bar; - | ^^^ use of unresolved module or unlinked crate `foo` +LL | use foo::beans; + | ^^^ could not find `foo` in the crate root | -help: you might be missing a crate named `foo`, add it to your project and import it in your code - | -LL + extern crate foo; +help: a module with a similar name exists | +LL | use food::beans; + | + error[E0432]: unresolved import `bar::Baz` - --> $DIR/unresolved-import.rs:8:5 + --> $DIR/unresolved-import.rs:12:5 | LL | use bar::Baz as x; | ^^^^^---^^^^^ @@ -24,7 +24,7 @@ LL + use bar::Bar as x; | error[E0432]: unresolved import `food::baz` - --> $DIR/unresolved-import.rs:14:5 + --> $DIR/unresolved-import.rs:18:5 | LL | use food::baz; | ^^^^^^--- @@ -32,7 +32,7 @@ LL | use food::baz; | no `baz` in `food` | note: module `food::zug::baz` exists but is inaccessible - --> $DIR/unresolved-import.rs:34:9 + --> $DIR/unresolved-import.rs:38:9 | LL | pub mod baz { | ^^^^^^^^^^^ not accessible @@ -43,7 +43,7 @@ LL + use food::bag; | error[E0432]: unresolved import `food::beens` - --> $DIR/unresolved-import.rs:20:12 + --> $DIR/unresolved-import.rs:24:12 | LL | use food::{beens as Foo}; | -----^^^^^^^ @@ -57,7 +57,7 @@ LL + use food::{beans as Foo}; | error[E0432]: unresolved import `MyEnum` - --> $DIR/unresolved-import.rs:47:9 + --> $DIR/unresolved-import.rs:51:9 | LL | use MyEnum::*; | ^^^^^^ @@ -68,7 +68,7 @@ LL | use self::MyEnum::*; | ++++++ error[E0432]: unresolved import `Enum` - --> $DIR/unresolved-import.rs:58:9 + --> $DIR/unresolved-import.rs:62:9 | LL | use Enum::*; | ^^^^ From b7996518b94f7917fd3ffa833aef3c9ff228ab2f Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Mon, 21 Sep 2026 04:10:45 -0500 Subject: [PATCH 08/27] add workaround to read `Rc` strong/weak counts --- src/etc/lldb_providers.py | 48 +++++++++++++++++++---------- tests/debuginfo/rc_arc.rs | 4 +-- tests/debuginfo/strings-and-strs.rs | 4 +-- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 2791dae3600b0..431d450885638 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -20,7 +20,6 @@ eBasicTypeUnsignedLongLong, eBasicTypeUnsignedShort, eFormatChar, - eTypeIsInteger, ) from rust_types import is_tuple_fields @@ -171,12 +170,6 @@ def unwrap_unique_or_non_null(unique_or_nonnull: SBValue) -> SBValue: return ptr if ptr.TypeIsPointerType() else ptr.GetChildAtIndex(0) -def unwrap_scalar_wrappers(wrapper: SBValue) -> SBValue: - while (wrapper.type.GetTypeFlags() & eTypeIsInteger) == 0: - wrapper = wrapper.GetChildAtIndex(0) - return wrapper - - class DefaultSyntheticProvider: def __init__(self, valobj: SBValue, _dict: LLDBOpaque): # logger = Logger.Logger() @@ -1545,9 +1538,17 @@ def has_children(self) -> bool: def StdRcSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: - strong = valobj.GetChildMemberWithName("strong").GetValueAsUnsigned() - weak = valobj.GetChildMemberWithName("weak").GetValueAsUnsigned() - return "strong={}, weak={}".format(strong, weak) + strong = valobj.GetChildMemberWithName("strong") + weak = valobj.GetChildMemberWithName("weak") + + if not (strong.IsValid() and weak.IsValid()): + strong = "?" + weak = "?" + else: + strong = strong.GetValueAsUnsigned() + weak = weak.GetValueAsUnsigned() + + return f"strong={strong}, weak={weak}" class StdRcSyntheticProvider: @@ -1570,8 +1571,24 @@ def __init__(self, valobj: SBValue, _dict: LLDBOpaque, is_atomic: bool = False): self.value = self.ptr.GetChildMemberWithName("data" if is_atomic else "value") - self.strong = unwrap_scalar_wrappers(self.ptr.GetChildMemberWithName("strong")) - self.weak = unwrap_scalar_wrappers(self.ptr.GetChildMemberWithName("weak")) + # infallibly gets an unsigned integer type of at least 64 bits. We don't need to worry about + # whether or not `usize` is actually smaller than that since we don't ever display the + # underlying type to the user anyway + usize_type = valobj.GetTarget().GetBasicType(eBasicTypeUnsignedLongLong) + + self.strong = self.ptr.GetChildMemberWithName("strong").Cast(usize_type) + self.weak = self.ptr.GetChildMemberWithName("weak").Cast(usize_type) + + # If the usize type isn't valid due to llvm/llvm-project#196812, not even the type's fields + # will populate. Luckily, `RcInner` is `#[repr(C)]`, so we can infallibly find the strong + # and weak values in memory + if not self.strong.IsValid() or not self.weak.IsValid(): + raw_ptr = self.ptr.Cast(usize_type.GetPointerType()) + addr = raw_ptr.GetValueAsAddress() + self.strong = self.valobj.CreateValueFromAddress("strong", addr, usize_type) + self.weak = self.valobj.CreateValueFromAddress( + "weak", addr + usize_type.GetByteSize(), usize_type + ) self.value_builder = ValueBuilder(valobj) @@ -1594,15 +1611,14 @@ def get_child_at_index(self, index: int) -> Optional[SBValue]: if index == 0: return self.value if index == 1: - return self.value_builder.from_uint("strong", self.strong_count) + return self.strong if index == 2: - return self.value_builder.from_uint("weak", self.weak_count) + return self.weak return None def update(self): - self.strong_count = self.strong.GetValueAsUnsigned() - self.weak_count = self.weak.GetValueAsUnsigned() - 1 + pass def has_children(self) -> bool: return True diff --git a/tests/debuginfo/rc_arc.rs b/tests/debuginfo/rc_arc.rs index b22b7e0d1611d..4399dfe54e7a5 100644 --- a/tests/debuginfo/rc_arc.rs +++ b/tests/debuginfo/rc_arc.rs @@ -18,9 +18,9 @@ //@ lldb-command:run //@ lldb-command:v rc -//@ lldb-check:[...] strong=11, weak=1 { value = 111 } +//@ lldb-check:[...] strong=11, weak=2 { value = 111 } //@ lldb-command:v arc -//@ lldb-check:[...] strong=21, weak=1 { data = 222 } +//@ lldb-check:[...] strong=21, weak=2 { data = 222 } // === CDB TESTS ================================================================================== diff --git a/tests/debuginfo/strings-and-strs.rs b/tests/debuginfo/strings-and-strs.rs index 692c75bbfbc9d..e081bf0a2ee8e 100644 --- a/tests/debuginfo/strings-and-strs.rs +++ b/tests/debuginfo/strings-and-strs.rs @@ -43,7 +43,7 @@ //@ lldb-check:[...] str_in_tuple = ("Hello", "World") //@ lldb-command:v str_in_rc -//@ lldb-check:[...] str_in_rc = strong=1, weak=0 { value = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } +//@ lldb-check:[...] str_in_rc = strong=1, weak=1 { value = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } //@ lldb-command:v box_str //@ lldb-check:[...] box_str = "World" { [0] = 'W' [1] = 'o' [2] = 'r' [3] = 'l' [4] = 'd' } @@ -56,7 +56,7 @@ // lldb-command:v rc_str // ignore-tidy-linelength -// lldb-check:(alloc::rc::Rc) rc_str = strong=1, weak=0 { value = "World" } +// lldb-check:(alloc::rc::Rc) rc_str = strong=1, weak=1 { value = "World" } #![allow(unused_variables)] From b2894fa5270738af6ddc7dca355581de4ca16add Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 21 Sep 2026 11:24:39 -0400 Subject: [PATCH 09/27] ci: make musl.sh look for patches next to the script Instead of hardcoding the container layout assuming that the local patches are in /build, look for them next to the script. This helps downstream distros to run this script if their container layout is different and they don't put the musl working dir in /build. This might be an unexpected change for some dowstream distros that were using this script from another directory,but arranged for the patches to still be in `/build` --- src/ci/docker/scripts/musl.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh index 370b243dd4551..2402ec6ae34bf 100644 --- a/src/ci/docker/scripts/musl.sh +++ b/src/ci/docker/scripts/musl.sh @@ -78,12 +78,14 @@ EOF # project, so we just apply them directly on top of the version we were distributing already. The # patches should be removed once we upgrade to musl >= 1.2.7. # + SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" && pwd) + MUSL_PATCH_DIR="$SCRIPT_DIR" # Advisory: https://www.openwall.com/lists/oss-security/2026/04/09/19 # Patches: https://www.openwall.com/lists/musl/2026/04/03/2/1 - patch -p1 -d $MUSL Date: Tue, 22 Sep 2026 09:40:09 +0800 Subject: [PATCH 10/27] Document safety requirements for intrinsic fallbacks --- library/core/src/intrinsics/fallback.rs | 27 +++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/library/core/src/intrinsics/fallback.rs b/library/core/src/intrinsics/fallback.rs index d2794a5b1202d..3e6829ab16b18 100644 --- a/library/core/src/intrinsics/fallback.rs +++ b/library/core/src/intrinsics/fallback.rs @@ -112,8 +112,13 @@ const impl CarryingMulAdd for i128 { #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] pub const trait DisjointBitOr: Copy + 'static { - /// See [`super::disjoint_bitor`]; we just need the trait indirection to handle - /// different types since calling intrinsics with generics doesn't work. + /// We just need the trait indirection to handle different types + /// since calling intrinsics with generics doesn't work. + /// + /// # Safety + /// + /// See [`super::disjoint_bitor`], with `self` corresponding to `a` and + /// `other` corresponding to `b`. unsafe fn disjoint_bitor(self, other: Self) -> Self; } macro_rules! zero { @@ -149,12 +154,22 @@ impl_disjoint_bitor! { #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] pub const trait FunnelShift: Copy + 'static { - /// See [`super::unchecked_funnel_shl`]; we just need the trait indirection to handle - /// different types since calling intrinsics with generics doesn't work. + /// We just need the trait indirection to handle different types + /// since calling intrinsics with generics doesn't work. + /// + /// # Safety + /// + /// See [`super::unchecked_funnel_shl`], with `self` corresponding to `a` and + /// `right` corresponding to `b`. unsafe fn unchecked_funnel_shl(self, right: Self, shift: u32) -> Self; - /// See [`super::unchecked_funnel_shr`]; we just need the trait indirection to handle - /// different types since calling intrinsics with generics doesn't work. + /// We just need the trait indirection to handle different types + /// since calling intrinsics with generics doesn't work. + /// + /// # Safety + /// + /// See [`super::unchecked_funnel_shr`], with `self` corresponding to `a` and + /// `right` corresponding to `b`. unsafe fn unchecked_funnel_shr(self, right: Self, shift: u32) -> Self; } From 391ff9139c54d4b4a8fd62c9c83ac8663da3dbb5 Mon Sep 17 00:00:00 2001 From: Yukang Date: Fri, 25 Sep 2026 09:42:33 +0800 Subject: [PATCH 11/27] Add regression test for lock guard must-use suggestions --- .../must-use-lock-guard-issue-162843.rs | 15 +++++ .../must-use-lock-guard-issue-162843.stderr | 55 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/ui/lint/unused/must-use-lock-guard-issue-162843.rs create mode 100644 tests/ui/lint/unused/must-use-lock-guard-issue-162843.stderr diff --git a/tests/ui/lint/unused/must-use-lock-guard-issue-162843.rs b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.rs new file mode 100644 index 0000000000000..10ab5d75a05fa --- /dev/null +++ b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.rs @@ -0,0 +1,15 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/162843. +//! Ignoring a synchronization guard should suggest a binding that keeps the lock held. +#![deny(unused_must_use)] + +use std::sync::{Mutex, RwLock}; + +fn main() { + let mutex = Mutex::new(0); + mutex.lock().unwrap(); //~ ERROR unused `std::sync::MutexGuard` that must be used + mutex.lock(); //~ ERROR unused `Result` that must be used + + let rwlock = RwLock::new(0); + rwlock.read().unwrap(); //~ ERROR unused `std::sync::RwLockReadGuard` that must be used + rwlock.write().unwrap(); //~ ERROR unused `std::sync::RwLockWriteGuard` that must be used +} diff --git a/tests/ui/lint/unused/must-use-lock-guard-issue-162843.stderr b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.stderr new file mode 100644 index 0000000000000..0e06555d20f9d --- /dev/null +++ b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.stderr @@ -0,0 +1,55 @@ +error: unused `std::sync::MutexGuard` that must be used + --> $DIR/must-use-lock-guard-issue-162843.rs:9:5 + | +LL | mutex.lock().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: if unused the Mutex will immediately unlock +note: the lint level is defined here + --> $DIR/must-use-lock-guard-issue-162843.rs:3:9 + | +LL | #![deny(unused_must_use)] + | ^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = mutex.lock().unwrap(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must-use-lock-guard-issue-162843.rs:10:5 + | +LL | mutex.lock(); + | ^^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = mutex.lock(); + | +++++++ + +error: unused `std::sync::RwLockReadGuard` that must be used + --> $DIR/must-use-lock-guard-issue-162843.rs:13:5 + | +LL | rwlock.read().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if unused the RwLock will immediately unlock +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = rwlock.read().unwrap(); + | +++++++ + +error: unused `std::sync::RwLockWriteGuard` that must be used + --> $DIR/must-use-lock-guard-issue-162843.rs:14:5 + | +LL | rwlock.write().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if unused the RwLock will immediately unlock +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = rwlock.write().unwrap(); + | +++++++ + +error: aborting due to 4 previous errors + From e47397562a314cd744e354899738e2b4baf9a713 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 25 Sep 2026 15:30:27 +1000 Subject: [PATCH 12/27] Remove `rustc_codegen_ssa::back::write::Diagnostic` It's a cut down version of `rustc_errors::DiagInner` that avoids `Span`. It exists because `Span` used to not impl `Send` and so couldn't be sent from codegen threads to the main thread. But that's no longer true and we can send `DiagInner`s directly now. --- compiler/rustc_codegen_ssa/src/back/write.rs | 78 ++------------------ 1 file changed, 7 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 147e4c43c7103..d53f6278fa253 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -4,15 +4,15 @@ use std::panic::AssertUnwindSafe; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; -use std::{assert_matches, fs, io, mem, str, thread}; +use std::{assert_matches, fs, io, str, thread}; use rustc_abi::Size; use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_errors::emitter::Emitter; use rustc_errors::{ - Diag, DiagArgMap, DiagCtxt, DiagCtxtHandle, DiagMessage, ErrCode, FatalError, FatalErrorMarker, - Level, MultiSpan, Style, Sublevel, Suggestions, catch_fatal_errors, + Diag, DiagCtxt, DiagCtxtHandle, DiagInner, FatalError, FatalErrorMarker, Level, + catch_fatal_errors, }; use rustc_fs_util::link_or_copy; use rustc_incremental::{ @@ -1191,31 +1191,6 @@ pub(crate) enum ThinLtoMessage { /// process another codegen unit. pub struct CguMessage; -// A cut-down version of `rustc_errors::DiagInner` that impls `Send`, which -// can be used to send diagnostics from codegen threads to the main thread. -// It's missing the following fields from `rustc_errors::DiagInner`. -// - `span`: it doesn't impl `Send`. -// - `suggestions`: it doesn't impl `Send`, and isn't used for codegen -// diagnostics. -// - `is_lint`: lints aren't relevant during codegen. -// - `emitted_at`: not used for codegen diagnostics. -struct Diagnostic { - span: Vec, - level: Level, - messages: Vec<(DiagMessage, Style)>, - code: Option, - children: Vec, - args: DiagArgMap, -} - -// A cut-down version of `rustc_errors::Subdiag` that impls `Send`. It's -// missing the following fields from `rustc_errors::Subdiag`. -// - `span`: it doesn't impl `Send`. -struct Subdiagnostic { - level: Sublevel, - messages: Vec<(DiagMessage, Style)>, -} - #[derive(PartialEq, Clone, Copy, Debug)] enum MainThreadState { /// Doing nothing. @@ -1952,7 +1927,7 @@ fn spawn_thin_lto_work( } enum SharedEmitterMessage { - Diagnostic(Diagnostic), + Diagnostic(DiagInner), InlineAsmError(InlineAsmError), Fatal(String), } @@ -1990,29 +1965,8 @@ impl SharedEmitter { } impl Emitter for SharedEmitter { - fn emit_diagnostic(&mut self, mut diag: rustc_errors::DiagInner) { - // Check that we aren't missing anything interesting when converting to - // the cut-down local `DiagInner`. - assert!(!diag.span.has_span_labels()); - assert_eq!(diag.suggestions, Suggestions::Enabled(vec![])); - assert_eq!(diag.is_lint, None); - // No sensible check for `diag.emitted_at`. - - let args = mem::take(&mut diag.args); - drop( - self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - span: diag.span.primary_spans().iter().map(|span| span.data()).collect::>(), - level: diag.level(), - messages: diag.messages, - code: diag.code, - children: diag - .children - .into_iter() - .map(|child| Subdiagnostic { level: child.level, messages: child.messages }) - .collect(), - args, - })), - ); + fn emit_diagnostic(&mut self, diag: DiagInner) { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(diag))); } fn source_map(&self) -> Option<&SourceMap> { @@ -2037,26 +1991,8 @@ impl SharedEmitterMain { match message { Ok(SharedEmitterMessage::Diagnostic(diag)) => { - // The diagnostic has been received on the main thread. - // Convert it back to a full `Diagnostic` and emit. let dcx = sess.dcx(); - let mut d = - rustc_errors::DiagInner::new_with_messages(diag.level, diag.messages); - d.span = MultiSpan::from_spans( - diag.span.into_iter().map(|span| span.span()).collect(), - ); - d.code = diag.code; // may be `None`, that's ok - d.children = diag - .children - .into_iter() - .map(|sub| rustc_errors::Subdiag { - level: sub.level, - messages: sub.messages, - span: MultiSpan::new(), - }) - .collect(); - d.args = diag.args; - dcx.emit_diagnostic(d); + dcx.emit_diagnostic(diag); sess.dcx().abort_if_errors(); } Ok(SharedEmitterMessage::InlineAsmError(inner)) => { From f01eea524cbbe0168703fef7a0df23a4fa96ca69 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 25 Sep 2026 15:42:16 +1000 Subject: [PATCH 13/27] Remove the `SpanData` in `InlineAsmError` By just storing `lo` and `hi` instead. --- compiler/rustc_codegen_llvm/src/back/write.rs | 17 +++++++---------- compiler/rustc_codegen_ssa/src/back/write.rs | 14 ++++++++++---- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index dd2dbdff6b82c..c18759bc65076 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -22,7 +22,7 @@ use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath}; -use rustc_span::{BytePos, InnerSpan, Pos, RemapPathScopeComponents, SpanData, SyntaxContext}; +use rustc_span::{BytePos, DUMMY_SP, InnerSpan, Pos, RemapPathScopeComponents}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; @@ -406,15 +406,12 @@ fn report_inline_asm( // In LTO build we may get srcloc values from other crates which are invalid // since they use a different source map. To be safe we just suppress these // in LTO builds. - let span = if cookie == 0 || matches!(cgcx.lto, Lto::Fat | Lto::Thin) { - SpanData::default() + let (lo, hi) = if cookie == 0 || matches!(cgcx.lto, Lto::Fat | Lto::Thin) { + (DUMMY_SP.lo(), DUMMY_SP.hi()) } else { - SpanData { - lo: BytePos::from_u32(cookie as u32), - hi: BytePos::from_u32((cookie >> 32) as u32), - ctxt: SyntaxContext::root(), - parent: None, - } + let lo = BytePos::from_u32(cookie as u32); + let hi = BytePos::from_u32((cookie >> 32) as u32); + (lo, hi) }; let level = match level { llvm::DiagnosticLevel::Error => Level::Error, @@ -422,7 +419,7 @@ fn report_inline_asm( llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note, }; let msg = msg.trim_prefix("error: ").to_string(); - InlineAsmError { span, msg, level, source } + InlineAsmError { lo, hi, msg, level, source } } unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index d53f6278fa253..d3c8110025185 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -27,7 +27,7 @@ use rustc_session::config::{ }; use rustc_session::{IncrCompSession, Session}; use rustc_span::source_map::SourceMap; -use rustc_span::{FileName, InnerSpan, Span, SpanData, bug}; +use rustc_span::{BytePos, FileName, InnerSpan, Span, SyntaxContext, bug}; use rustc_structures::CrateType; use rustc_target::spec::{MergeFunctions, SanitizerSet}; use tracing::debug; @@ -1933,7 +1933,12 @@ enum SharedEmitterMessage { } pub struct InlineAsmError { - pub span: SpanData, + // We store byte positions rather than a `Span` because this error is created on a codegen + // thread which doesn't have access to the span interner within the session globals. The byte + // positions are converted to a `Span` once they reach a thread with access to the span + // interner. + pub lo: BytePos, + pub hi: BytePos, pub msg: String, pub level: Level, pub source: Option<(String, Vec)>, @@ -1998,8 +2003,9 @@ impl SharedEmitterMain { Ok(SharedEmitterMessage::InlineAsmError(inner)) => { assert_matches!(inner.level, Level::Error | Level::Warning | Level::Note); let mut err = Diag::new(sess.dcx(), inner.level, inner.msg); - if !inner.span.is_dummy() { - err.span(inner.span.span()); + let span = Span::new(inner.lo, inner.hi, SyntaxContext::root(), None); + if !span.is_dummy() { + err.span(span); } // Point to the generated assembly if it is available. From d35f696938801bcdada7ce07b691610579a61265 Mon Sep 17 00:00:00 2001 From: Yukang Date: Fri, 25 Sep 2026 12:17:55 +0800 Subject: [PATCH 14/27] fix unused_must_use suggestion for scenario which may need to keep value --- compiler/rustc_lint/src/diagnostics.rs | 21 ++++++ compiler/rustc_lint/src/unused/must_use.rs | 73 +++++++++++++++---- .../lint/unused/must-use-box-from-raw.stderr | 6 +- .../must-use-lock-guard-issue-162843.fixed | 41 +++++++++++ .../must-use-lock-guard-issue-162843.rs | 40 ++++++++-- .../must-use-lock-guard-issue-162843.stderr | 56 ++++++++------ .../must_use-result-unit-uninhabited.stderr | 6 +- tests/ui/lint/unused/unused-async.stderr | 12 +-- 8 files changed, 197 insertions(+), 58 deletions(-) create mode 100644 tests/ui/lint/unused/must-use-lock-guard-issue-162843.fixed diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index 98a11f8e1df08..464423b303f4f 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -2709,6 +2709,27 @@ pub(crate) enum UnusedDefSuggestion { #[suggestion_part(code = ";")] after_span: Span, }, + #[suggestion( + "use `let _unused = ...` to ignore the resulting value without dropping it immediately", + style = "verbose", + code = "let _unused = ", + applicability = "maybe-incorrect" + )] + BindingExpr { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + "use `let _unused = ...` to ignore the resulting value without dropping it immediately", + style = "verbose", + applicability = "maybe-incorrect" + )] + BindingBlockTailExpr { + #[suggestion_part(code = "let _unused = ")] + before_span: Span, + #[suggestion_part(code = ";")] + after_span: Span, + }, } // Needed because of def_path_str diff --git a/compiler/rustc_lint/src/unused/must_use.rs b/compiler/rustc_lint/src/unused/must_use.rs index dab842a688ae1..89e3981b965f2 100644 --- a/compiler/rustc_lint/src/unused/must_use.rs +++ b/compiler/rustc_lint/src/unused/must_use.rs @@ -299,6 +299,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { return; } + let ty = cx.typeck_results().expr_ty(expr); + let needs_binding = ty.has_significant_drop(cx.tcx, cx.typing_env()); + if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind && let ty = cx.typeck_results().expr_ty(await_expr) && let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: future_def_id }, .. }) = ty.kind() @@ -314,6 +317,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { "output of future returned by ", "", expr_is_from_block, + needs_binding, ) { // We have a bare `foo().await;` on an opaque type from an async function that was @@ -321,19 +325,26 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { return; } - let ty = cx.typeck_results().expr_ty(expr); - let must_use_result = is_ty_must_use(cx, ty, expr); let type_lint_emitted_or_trivial = match must_use_result { IsTyMustUse::Yes(path) => { - emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); + emit_must_use_untranslated( + cx, + &path, + "", + "", + 1, + false, + expr_is_from_block, + needs_binding, + ); true } IsTyMustUse::Trivial => true, IsTyMustUse::No => false, }; - let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block); + let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block, needs_binding); if !fn_warned && type_lint_emitted_or_trivial { // We don't warn about unused unit or uninhabited types. @@ -415,7 +426,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { /// Checks if `expr` is a \[method\] call expression marked as `#[must_use]` and emits a lint if so. /// Returns `true` if the lint has been emitted. -fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expr_is_from_block: bool) -> bool { +fn check_fn_must_use( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + expr_is_from_block: bool, + needs_binding: bool, +) -> bool { let maybe_def_id = match expr.kind { hir::ExprKind::Call(callee, _) => { if let hir::ExprKind::Path(ref qpath) = callee.kind @@ -434,9 +450,15 @@ fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expr_is_from_bl }; match maybe_def_id { - Some(def_id) => { - check_must_use_def(cx, def_id, expr.span, "return value of ", "", expr_is_from_block) - } + Some(def_id) => check_must_use_def( + cx, + def_id, + expr.span, + "return value of ", + "", + expr_is_from_block, + needs_binding, + ), None => false, } } @@ -455,6 +477,7 @@ fn check_must_use_def( descr_pre_path: &str, descr_post_path: &str, expr_is_from_block: bool, + needs_binding: bool, ) -> bool { is_def_must_use(cx, def_id, span) .map(|must_use_path| { @@ -466,6 +489,7 @@ fn check_must_use_def( 1, false, expr_is_from_block, + needs_binding, ) }) .is_some() @@ -480,6 +504,7 @@ fn emit_must_use_untranslated( plural_len: usize, is_inner: bool, expr_is_from_block: bool, + needs_binding: bool, ) { let plural_suffix = pluralize!(plural_len); @@ -494,6 +519,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::Pinned(path) => { @@ -506,6 +532,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::Opaque(path) => { @@ -518,6 +545,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::TraitObject(path) => { @@ -530,6 +558,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::TupleElement(elems) => { @@ -543,6 +572,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } } @@ -556,6 +586,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::ControlFlow(path) => { @@ -568,6 +599,7 @@ fn emit_must_use_untranslated( plural_len, true, expr_is_from_block, + needs_binding, ); } MustUsePath::Array(path, len) => { @@ -580,6 +612,7 @@ fn emit_must_use_untranslated( plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), true, expr_is_from_block, + needs_binding, ); } MustUsePath::Closure(span) => { @@ -615,14 +648,24 @@ fn emit_must_use_untranslated( cx, def_id: *def_id, note: *reason, - suggestion: (!is_inner).then_some(if expr_is_from_block { - UnusedDefSuggestion::BlockTailExpr { - before_span: suggestion_span.shrink_to_lo(), - after_span: suggestion_span.shrink_to_hi(), - } + suggestion: if is_inner { + None } else { - UnusedDefSuggestion::NormalExpr { span: suggestion_span.shrink_to_lo() } - }), + let lo = suggestion_span.shrink_to_lo(); + let hi = suggestion_span.shrink_to_hi(); + Some(match (needs_binding, expr_is_from_block) { + (true, true) => UnusedDefSuggestion::BindingBlockTailExpr { + before_span: lo, + after_span: hi, + }, + (true, false) => UnusedDefSuggestion::BindingExpr { span: lo }, + (false, true) => UnusedDefSuggestion::BlockTailExpr { + before_span: lo, + after_span: hi, + }, + (false, false) => UnusedDefSuggestion::NormalExpr { span: lo }, + }) + }, }, ); } diff --git a/tests/ui/lint/unused/must-use-box-from-raw.stderr b/tests/ui/lint/unused/must-use-box-from-raw.stderr index 4898db7fe3ddd..7eab99e97cd82 100644 --- a/tests/ui/lint/unused/must-use-box-from-raw.stderr +++ b/tests/ui/lint/unused/must-use-box-from-raw.stderr @@ -10,10 +10,10 @@ note: the lint level is defined here | LL | #![warn(unused_must_use)] | ^^^^^^^^^^^^^^^ -help: use `let _ = ...` to ignore the resulting value +help: use `let _unused = ...` to ignore the resulting value without dropping it immediately | -LL | let _ = Box::from_raw(ptr); - | +++++++ +LL | let _unused = Box::from_raw(ptr); + | +++++++++++++ warning: 1 warning emitted diff --git a/tests/ui/lint/unused/must-use-lock-guard-issue-162843.fixed b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.fixed new file mode 100644 index 0000000000000..13289df3677fd --- /dev/null +++ b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.fixed @@ -0,0 +1,41 @@ +//@ run-rustfix +//! Regression test for https://github.com/rust-lang/rust/issues/162843. +//! Must-use values with nontrivial drop should suggest a binding that preserves drop timing. +#![deny(unused_must_use)] + +use std::sync::Mutex; + +struct Guard; + +impl Drop for Guard { + fn drop(&mut self) {} +} + +#[must_use] +fn guard() -> Guard { + Guard +} + +#[must_use] +fn value() -> u8 { + 0 +} + +#[must_use] +fn vec_values() -> Vec { + vec![1, 2, 3] +} + +#[must_use] +fn box_value() -> Box { + Box::new(0) +} + +fn main() { + let mutex = Mutex::new(0); + let _unused = mutex.lock().unwrap(); //~ ERROR unused `std::sync::MutexGuard` that must be used + let _unused = guard(); //~ ERROR unused return value of `guard` that must be used + let _ = value(); //~ ERROR unused return value of `value` that must be used + let _ = vec_values(); //~ ERROR unused return value of `vec_values` that must be used + let _ = box_value(); //~ ERROR unused return value of `box_value` that must be used +} diff --git a/tests/ui/lint/unused/must-use-lock-guard-issue-162843.rs b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.rs index 10ab5d75a05fa..1b0de8242a7a7 100644 --- a/tests/ui/lint/unused/must-use-lock-guard-issue-162843.rs +++ b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.rs @@ -1,15 +1,41 @@ +//@ run-rustfix //! Regression test for https://github.com/rust-lang/rust/issues/162843. -//! Ignoring a synchronization guard should suggest a binding that keeps the lock held. +//! Must-use values with nontrivial drop should suggest a binding that preserves drop timing. #![deny(unused_must_use)] -use std::sync::{Mutex, RwLock}; +use std::sync::Mutex; + +struct Guard; + +impl Drop for Guard { + fn drop(&mut self) {} +} + +#[must_use] +fn guard() -> Guard { + Guard +} + +#[must_use] +fn value() -> u8 { + 0 +} + +#[must_use] +fn vec_values() -> Vec { + vec![1, 2, 3] +} + +#[must_use] +fn box_value() -> Box { + Box::new(0) +} fn main() { let mutex = Mutex::new(0); mutex.lock().unwrap(); //~ ERROR unused `std::sync::MutexGuard` that must be used - mutex.lock(); //~ ERROR unused `Result` that must be used - - let rwlock = RwLock::new(0); - rwlock.read().unwrap(); //~ ERROR unused `std::sync::RwLockReadGuard` that must be used - rwlock.write().unwrap(); //~ ERROR unused `std::sync::RwLockWriteGuard` that must be used + guard(); //~ ERROR unused return value of `guard` that must be used + value(); //~ ERROR unused return value of `value` that must be used + vec_values(); //~ ERROR unused return value of `vec_values` that must be used + box_value(); //~ ERROR unused return value of `box_value` that must be used } diff --git a/tests/ui/lint/unused/must-use-lock-guard-issue-162843.stderr b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.stderr index 0e06555d20f9d..8fa5c693f2561 100644 --- a/tests/ui/lint/unused/must-use-lock-guard-issue-162843.stderr +++ b/tests/ui/lint/unused/must-use-lock-guard-issue-162843.stderr @@ -1,55 +1,63 @@ error: unused `std::sync::MutexGuard` that must be used - --> $DIR/must-use-lock-guard-issue-162843.rs:9:5 + --> $DIR/must-use-lock-guard-issue-162843.rs:36:5 | LL | mutex.lock().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^ | = note: if unused the Mutex will immediately unlock note: the lint level is defined here - --> $DIR/must-use-lock-guard-issue-162843.rs:3:9 + --> $DIR/must-use-lock-guard-issue-162843.rs:4:9 | LL | #![deny(unused_must_use)] | ^^^^^^^^^^^^^^^ -help: use `let _ = ...` to ignore the resulting value +help: use `let _unused = ...` to ignore the resulting value without dropping it immediately | -LL | let _ = mutex.lock().unwrap(); - | +++++++ +LL | let _unused = mutex.lock().unwrap(); + | +++++++++++++ -error: unused `Result` that must be used - --> $DIR/must-use-lock-guard-issue-162843.rs:10:5 +error: unused return value of `guard` that must be used + --> $DIR/must-use-lock-guard-issue-162843.rs:37:5 | -LL | mutex.lock(); - | ^^^^^^^^^^^^ +LL | guard(); + | ^^^^^^^ + | +help: use `let _unused = ...` to ignore the resulting value without dropping it immediately + | +LL | let _unused = guard(); + | +++++++++++++ + +error: unused return value of `value` that must be used + --> $DIR/must-use-lock-guard-issue-162843.rs:38:5 + | +LL | value(); + | ^^^^^^^ | - = note: this `Result` may be an `Err` variant, which should be handled help: use `let _ = ...` to ignore the resulting value | -LL | let _ = mutex.lock(); +LL | let _ = value(); | +++++++ -error: unused `std::sync::RwLockReadGuard` that must be used - --> $DIR/must-use-lock-guard-issue-162843.rs:13:5 +error: unused return value of `vec_values` that must be used + --> $DIR/must-use-lock-guard-issue-162843.rs:39:5 | -LL | rwlock.read().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | vec_values(); + | ^^^^^^^^^^^^ | - = note: if unused the RwLock will immediately unlock help: use `let _ = ...` to ignore the resulting value | -LL | let _ = rwlock.read().unwrap(); +LL | let _ = vec_values(); | +++++++ -error: unused `std::sync::RwLockWriteGuard` that must be used - --> $DIR/must-use-lock-guard-issue-162843.rs:14:5 +error: unused return value of `box_value` that must be used + --> $DIR/must-use-lock-guard-issue-162843.rs:40:5 | -LL | rwlock.write().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | box_value(); + | ^^^^^^^^^^^ | - = note: if unused the RwLock will immediately unlock help: use `let _ = ...` to ignore the resulting value | -LL | let _ = rwlock.write().unwrap(); +LL | let _ = box_value(); | +++++++ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr index 230c97092b526..13c51a588fb03 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -75,10 +75,10 @@ LL | self.generate(); | ^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled -help: use `let _ = ...` to ignore the resulting value +help: use `let _unused = ...` to ignore the resulting value without dropping it immediately | -LL | let _ = self.generate(); - | +++++++ +LL | let _unused = self.generate(); + | +++++++++++++ error: aborting due to 7 previous errors diff --git a/tests/ui/lint/unused/unused-async.stderr b/tests/ui/lint/unused/unused-async.stderr index e93a40658f3c2..43bc73d2758c1 100644 --- a/tests/ui/lint/unused/unused-async.stderr +++ b/tests/ui/lint/unused/unused-async.stderr @@ -17,10 +17,10 @@ error: unused return value of `foo` that must be used LL | foo(); | ^^^^^ | -help: use `let _ = ...` to ignore the resulting value +help: use `let _unused = ...` to ignore the resulting value without dropping it immediately | -LL | let _ = foo(); - | +++++++ +LL | let _unused = foo(); + | +++++++++++++ error: unused output of future returned by `foo` that must be used --> $DIR/unused-async.rs:33:5 @@ -47,10 +47,10 @@ error: unused return value of `bar` that must be used LL | bar(); | ^^^^^ | -help: use `let _ = ...` to ignore the resulting value +help: use `let _unused = ...` to ignore the resulting value without dropping it immediately | -LL | let _ = bar(); - | +++++++ +LL | let _unused = bar(); + | +++++++++++++ error: unused implementer of `Future` that must be used --> $DIR/unused-async.rs:37:5 From 8f40c2a16bac9ce168bccda960ea7655bc1483c8 Mon Sep 17 00:00:00 2001 From: Yukang Date: Mon, 21 Sep 2026 10:11:17 +0800 Subject: [PATCH 15/27] Filter with accessbility before find match for name --- .../rustc_resolve/src/diagnostics/impls.rs | 36 ++++++++++++------- .../nested-module-typo-issue-131366.fixed | 10 ++++++ .../nested-module-typo-issue-131366.rs | 10 ++++++ .../nested-module-typo-issue-131366.stderr | 13 ++++++- 4 files changed, 56 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index ee13092abd68e..dfef1e7cb92cb 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1033,6 +1033,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Res::Def(k, _) => matches_kind(k), _ => false, }, + &|_| true, ) && let Res::Def(kind, mut def_id) = suggestion.res { if let DefKind::Ctor(_, _) = kind { @@ -1603,6 +1604,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope: &ParentScope<'ra>, ident: Ident, filter_fn: &impl Fn(Res) -> bool, + suggestion_filter: &impl Fn(&TypoSuggestion) -> bool, ) -> Option { let mut suggestions = Vec::new(); self.add_scope_set_candidates( @@ -1613,6 +1615,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { filter_fn, ); + // Some candidates cannot be decided from the `Res` alone (e.g. they need + // re-resolution or visibility checks), filter them out before picking the + // best name match. + suggestions.retain(suggestion_filter); + // Make sure error reporting is deterministic. suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); @@ -1965,6 +1972,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope, ident, is_expected, + &|_| true, ); self.add_typo_suggestion(err, suggestion, ident.span); self.detect_derive_attribute(err, ident, parent_scope, sugg_span); @@ -3034,18 +3042,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope, ident, &|res| matches!(res, Res::Def(DefKind::Mod | DefKind::Enum, _)), - ) - && let Ok(binding) = self.cm().resolve_ident_in_module( - ModuleOrUniformRoot::Module(module), - Ident::new(candidate.candidate, ident.span), - TypeNS, - parent_scope, - None, - ignore_decl, - ignore_import, - ) - && self.is_accessible_from(binding.vis(), parent_scope.module) - { + &|candidate| { + self.cm() + .resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + Ident::new(candidate.candidate, ident.span), + TypeNS, + parent_scope, + None, + ignore_decl, + ignore_import, + ) + .is_ok_and(|binding| { + self.is_accessible_from(binding.vis(), parent_scope.module) + }) + }, + ) { Some(( vec![(ident.span, Ident::new(candidate.candidate, ident.span).to_string())], format!( diff --git a/tests/ui/imports/nested-module-typo-issue-131366.fixed b/tests/ui/imports/nested-module-typo-issue-131366.fixed index a84119fe93a8b..ff1c4eb2532b2 100644 --- a/tests/ui/imports/nested-module-typo-issue-131366.fixed +++ b/tests/ui/imports/nested-module-typo-issue-131366.fixed @@ -33,4 +33,14 @@ macro_rules! import { import!(collections); //~^ ERROR unresolved import `local::collection` +mod accessible { + pub mod collections { + pub struct Item; + } + use collections as collection; +} +// The closer match is a private alias, so suggest the accessible module instead. +use accessible::collections::Item as AccessibleItem; +//~^ ERROR unresolved import `accessible::collectio` + fn main() {} diff --git a/tests/ui/imports/nested-module-typo-issue-131366.rs b/tests/ui/imports/nested-module-typo-issue-131366.rs index 2f269da41b209..2d0e81e2e0e3a 100644 --- a/tests/ui/imports/nested-module-typo-issue-131366.rs +++ b/tests/ui/imports/nested-module-typo-issue-131366.rs @@ -33,4 +33,14 @@ macro_rules! import { import!(collection); //~^ ERROR unresolved import `local::collection` +mod accessible { + pub mod collections { + pub struct Item; + } + use collections as collection; +} +// The closer match is a private alias, so suggest the accessible module instead. +use accessible::collectio::Item as AccessibleItem; +//~^ ERROR unresolved import `accessible::collectio` + fn main() {} diff --git a/tests/ui/imports/nested-module-typo-issue-131366.stderr b/tests/ui/imports/nested-module-typo-issue-131366.stderr index 922b9219a7854..1173fc1fb5c57 100644 --- a/tests/ui/imports/nested-module-typo-issue-131366.stderr +++ b/tests/ui/imports/nested-module-typo-issue-131366.stderr @@ -42,6 +42,17 @@ help: an enum with a similar name exists LL | use local::Choices::First; | + +error[E0432]: unresolved import `accessible::collectio` + --> $DIR/nested-module-typo-issue-131366.rs:43:17 + | +LL | use accessible::collectio::Item as AccessibleItem; + | ^^^^^^^^^ could not find `collectio` in `accessible` + | +help: a module with a similar name exists + | +LL | use accessible::collections::Item as AccessibleItem; + | ++ + error[E0432]: unresolved import `crate::root_modul` --> $DIR/nested-module-typo-issue-131366.rs:27:12 | @@ -64,6 +75,6 @@ help: a module with a similar name exists LL | import!(collections); | + -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0432`. From 284af39d64d0fa81b70650e3609e83a7998d1532 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 26 Sep 2026 18:04:48 +0200 Subject: [PATCH 16/27] Use a let-chain This whole section of the code deals with constructing an alternative layout, but large nesting makes the control flow seem more complex than it actually is. --- compiler/rustc_abi/src/layout.rs | 93 +++++++++++++++----------------- 1 file changed, 44 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index e8779d6ee6869..f808285edbef5 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -281,58 +281,53 @@ impl LayoutCalculator { // If the default field ordering does not give us a niche at the front then we do a second // run and bias niches to the right and then check which one is closer to one of the // struct's edges. - if let Ok(layout) = &layout { + if let Ok(layout) = &layout // Don't try to calculate an end-biased layout for unsizable structs, // otherwise we could end up with different layouts for // Foo and Foo which would break unsizing. - if !matches!(kind, StructKind::MaybeUnsized) { - if let Some(niche) = layout.largest_niche { - let head_space = niche.offset.bytes(); - let niche_len = niche.value.size(dl).bytes(); - let tail_space = layout.size.bytes() - head_space - niche_len; - - // This may end up doing redundant work if the niche is already in the last - // field (e.g. a trailing bool) and there is tail padding. But it's non-trivial - // to get the unpadded size so we try anyway. - if fields.len() > 1 && head_space != 0 && tail_space > 0 { - let alt_layout = self - .univariant_biased(fields, repr, kind, NicheBias::End) - .expect("alt layout should always work"); - let alt_niche = alt_layout - .largest_niche - .expect("alt layout should have a niche like the regular one"); - let alt_head_space = alt_niche.offset.bytes(); - let alt_niche_len = alt_niche.value.size(dl).bytes(); - let alt_tail_space = - alt_layout.size.bytes() - alt_head_space - alt_niche_len; - - debug_assert_eq!(layout.size.bytes(), alt_layout.size.bytes()); - - let prefer_alt_layout = - alt_head_space > head_space && alt_head_space > tail_space; - - debug!( - "sz: {}, default_niche_at: {}+{}, default_tail_space: {}, alt_niche_at/head_space: {}+{}, alt_tail: {}, num_fields: {}, better: {}\n\ - layout: {}\n\ - alt_layout: {}\n", - layout.size.bytes(), - head_space, - niche_len, - tail_space, - alt_head_space, - alt_niche_len, - alt_tail_space, - layout.fields.count(), - prefer_alt_layout, - self.format_field_niches(layout, fields), - self.format_field_niches(&alt_layout, fields), - ); - - if prefer_alt_layout { - return Ok(alt_layout); - } - } - } + && !matches!(kind, StructKind::MaybeUnsized) + && let Some(niche) = layout.largest_niche + && let head_space = niche.offset.bytes() + && let niche_len = niche.value.size(dl).bytes() + && let tail_space = layout.size.bytes() - head_space - niche_len + // This may end up doing redundant work if the niche is already in the last + // field (e.g. a trailing bool) and there is tail padding. But it's non-trivial + // to get the unpadded size so we try anyway. + && (fields.len() > 1 && head_space != 0 && tail_space > 0) + { + let alt_layout = self + .univariant_biased(fields, repr, kind, NicheBias::End) + .expect("alt layout should always work"); + let alt_niche = alt_layout + .largest_niche + .expect("alt layout should have a niche like the regular one"); + let alt_head_space = alt_niche.offset.bytes(); + let alt_niche_len = alt_niche.value.size(dl).bytes(); + let alt_tail_space = alt_layout.size.bytes() - alt_head_space - alt_niche_len; + + debug_assert_eq!(layout.size.bytes(), alt_layout.size.bytes()); + + let prefer_alt_layout = alt_head_space > head_space && alt_head_space > tail_space; + + debug!( + "sz: {}, default_niche_at: {}+{}, default_tail_space: {}, alt_niche_at/head_space: {}+{}, alt_tail: {}, num_fields: {}, better: {}\n\ + layout: {}\n\ + alt_layout: {}\n", + layout.size.bytes(), + head_space, + niche_len, + tail_space, + alt_head_space, + alt_niche_len, + alt_tail_space, + layout.fields.count(), + prefer_alt_layout, + self.format_field_niches(layout, fields), + self.format_field_niches(&alt_layout, fields), + ); + + if prefer_alt_layout { + return Ok(alt_layout); } } layout From d2b3f9b61f30c99ad8de91d8f5d2caa901a592cc Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 26 Sep 2026 18:12:02 +0200 Subject: [PATCH 17/27] Remove needless laziness Constructing these error variants is basically free --- compiler/rustc_abi/src/layout.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index f808285edbef5..89ee4a27e9c33 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1479,15 +1479,13 @@ where }; // Compute the size and alignment of the vector - let size = elt - .size - .checked_mul(count.as_u64(), dl) - .ok_or_else(|| LayoutCalculatorError::SizeOverflow)?; + let size = + elt.size.checked_mul(count.as_u64(), dl).ok_or(LayoutCalculatorError::SizeOverflow)?; let (repr, size, align) = match kind { SimdVectorKind::Scalable(number_of_vectors) => ( BackendRepr::SimdScalableVector { element, count, number_of_vectors }, size.checked_mul(number_of_vectors.0 as u64, dl) - .ok_or_else(|| LayoutCalculatorError::SizeOverflow)?, + .ok_or(LayoutCalculatorError::SizeOverflow)?, dl.rust_vector_align(size), ), // Non-power-of-two vectors have padding up to the next power-of-two. From 6c333a9e0dfc23d5ef889198e5198a814067e4b9 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 26 Sep 2026 18:16:17 +0200 Subject: [PATCH 18/27] Move complex clauses to the where clause Having the generic list span multiple lines is quite jarring, and imo easy to confuse with the parameter list at a glance. --- compiler/rustc_abi/src/layout.rs | 114 ++++++++++----------- compiler/rustc_abi/src/layout/coroutine.rs | 16 +-- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 89ee4a27e9c33..e867d4da7bf3e 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -239,20 +239,20 @@ impl LayoutCalculator { /// /// This uses dedicated code instead of [`Self::layout_of_struct_or_enum`], as coroutine /// fields may be shared between multiple variants (see the [`coroutine`] module for details). - pub fn coroutine< - 'a, - F: Deref> + fmt::Debug + Copy, - VariantIdx: Idx, - FieldIdx: Idx, - LocalIdx: Idx, - >( + pub fn coroutine<'a, F, VariantIdx, FieldIdx, LocalIdx>( &self, local_layouts: &IndexSlice, prefix_layouts: IndexVec, variant_fields: &IndexSlice>, storage_conflicts: &BitMatrix, tag_to_layout: impl Fn(Scalar) -> F, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + F: Deref> + fmt::Debug + Copy, + VariantIdx: Idx, + FieldIdx: Idx, + LocalIdx: Idx, + { coroutine::layout( self, local_layouts, @@ -263,17 +263,17 @@ impl LayoutCalculator { ) } - pub fn univariant< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + pub fn univariant<'a, FieldIdx, VariantIdx, F>( &self, fields: &IndexSlice, repr: &ReprOptions, kind: StructKind, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let dl = self.cx.data_layout(); let layout = self.univariant_biased(fields, repr, kind, NicheBias::Start); // Enums prefer niches close to the beginning or the end of the variants so that other @@ -333,12 +333,7 @@ impl LayoutCalculator { layout } - pub fn layout_of_struct_or_enum< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + pub fn layout_of_struct_or_enum<'a, FieldIdx, VariantIdx, F>( &self, repr: &ReprOptions, variants: &IndexSlice>, @@ -347,7 +342,12 @@ impl LayoutCalculator { discr_range_of_repr: impl Fn(RangeFrom, RangeToInclusive) -> (Integer, bool), discriminants: impl Iterator, always_sized: bool, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let (present_first, present_second) = { let mut present_variants = variants.iter_enumerated().filter_map(|(i, v)| { if !repr.inhibit_enum_layout_opt() && absent(v) { None } else { Some(i) } @@ -387,16 +387,16 @@ impl LayoutCalculator { } } - pub fn layout_of_union< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + pub fn layout_of_union<'a, FieldIdx, VariantIdx, F>( &self, repr: &ReprOptions, variants: &IndexSlice>, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let dl = self.cx.data_layout(); let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut max_repr_align = repr.align; @@ -515,12 +515,7 @@ impl LayoutCalculator { } /// single-variant enums are just structs, if you think about it - fn layout_of_struct< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + fn layout_of_struct<'a, FieldIdx, VariantIdx, F>( &self, repr: &ReprOptions, variants: &IndexSlice>, @@ -528,7 +523,12 @@ impl LayoutCalculator { is_special_no_niche: bool, always_sized: bool, present_first: VariantIdx, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { // Struct, or univariant enum equivalent to a struct. // (Typechecking will reject discriminant-sizing attrs.) @@ -568,18 +568,18 @@ impl LayoutCalculator { Ok(st) } - fn layout_of_enum< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + fn layout_of_enum<'a, FieldIdx, VariantIdx, F>( &self, repr: &ReprOptions, variants: &IndexSlice>, discr_range_of_repr: impl Fn(RangeFrom, RangeToInclusive) -> (Integer, bool), discriminants: impl Iterator, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let dl = self.cx.data_layout(); // bail if the enum has an incoherent repr that cannot be computed if repr.packed() { @@ -1073,18 +1073,18 @@ impl LayoutCalculator { Ok(best_layout) } - fn univariant_biased< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug + Copy, - >( + fn univariant_biased<'a, FieldIdx, VariantIdx, F>( &self, fields: &IndexSlice, repr: &ReprOptions, kind: StructKind, niche_bias: NicheBias, - ) -> LayoutCalculatorResult { + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug + Copy, + { let dl = self.cx.data_layout(); let pack = repr.pack; let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; @@ -1419,16 +1419,16 @@ impl LayoutCalculator { }) } - fn format_field_niches< - 'a, - FieldIdx: Idx, - VariantIdx: Idx, - F: Deref> + fmt::Debug, - >( + fn format_field_niches<'a, FieldIdx, VariantIdx, F>( &self, layout: &LayoutData, fields: &IndexSlice, - ) -> String { + ) -> String + where + FieldIdx: Idx, + VariantIdx: Idx, + F: Deref> + fmt::Debug, + { let dl = self.cx.data_layout(); let mut s = String::new(); for i in layout.fields.index_by_increasing_offset() { diff --git a/compiler/rustc_abi/src/layout/coroutine.rs b/compiler/rustc_abi/src/layout/coroutine.rs index fd68d06c93829..4a1cb5bc86fc4 100644 --- a/compiler/rustc_abi/src/layout/coroutine.rs +++ b/compiler/rustc_abi/src/layout/coroutine.rs @@ -136,20 +136,20 @@ fn coroutine_saved_local_eligibility> + core::fmt::Debug + Copy, - VariantIdx: Idx, - FieldIdx: Idx, - LocalIdx: Idx, ->( +pub(super) fn layout<'a, F, VariantIdx, FieldIdx, LocalIdx>( calc: &super::LayoutCalculator, local_layouts: &IndexSlice, mut prefix_layouts: IndexVec, variant_fields: &IndexSlice>, storage_conflicts: &BitMatrix, tag_to_layout: impl Fn(Scalar) -> F, -) -> super::LayoutCalculatorResult { +) -> super::LayoutCalculatorResult +where + F: core::ops::Deref> + core::fmt::Debug + Copy, + VariantIdx: Idx, + FieldIdx: Idx, + LocalIdx: Idx, +{ use SavedLocalEligibility::*; let (ineligible_locals, assignments) = From e1dfc384188c45b8b43e6de7e61bcc7377d56ceb Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 26 Sep 2026 18:58:47 +0200 Subject: [PATCH 19/27] Add `layout_of_` prefix to all the layout-calculating methods Improves consistency, and hopefully clarifies the purpose of previously-mysteriosly named methods like `univariant`. --- compiler/rustc_abi/src/layout.rs | 22 +++++++++++----------- compiler/rustc_abi/src/layout/coroutine.rs | 9 ++++++--- compiler/rustc_ty_utils/src/layout.rs | 14 +++++++------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index e867d4da7bf3e..a10548d1a6059 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -178,7 +178,7 @@ impl LayoutCalculator { Self { cx } } - pub fn array_like( + pub fn layout_of_array_like( &self, element: &LayoutData, count_if_sized: Option, // None for slices @@ -201,7 +201,7 @@ impl LayoutCalculator { }) } - pub fn scalable_vector_type( + pub fn layout_of_scalable_vector_type( &self, element: F, count: u64, @@ -220,7 +220,7 @@ impl LayoutCalculator { ) } - pub fn simd_type( + pub fn layout_of_simd_type( &self, element: F, count: u64, @@ -239,7 +239,7 @@ impl LayoutCalculator { /// /// This uses dedicated code instead of [`Self::layout_of_struct_or_enum`], as coroutine /// fields may be shared between multiple variants (see the [`coroutine`] module for details). - pub fn coroutine<'a, F, VariantIdx, FieldIdx, LocalIdx>( + pub fn layout_of_coroutine<'a, F, VariantIdx, FieldIdx, LocalIdx>( &self, local_layouts: &IndexSlice, prefix_layouts: IndexVec, @@ -263,7 +263,7 @@ impl LayoutCalculator { ) } - pub fn univariant<'a, FieldIdx, VariantIdx, F>( + pub fn layout_of_univariant<'a, FieldIdx, VariantIdx, F>( &self, fields: &IndexSlice, repr: &ReprOptions, @@ -275,7 +275,7 @@ impl LayoutCalculator { F: Deref> + fmt::Debug + Copy, { let dl = self.cx.data_layout(); - let layout = self.univariant_biased(fields, repr, kind, NicheBias::Start); + let layout = self.layout_of_univariant_biased(fields, repr, kind, NicheBias::Start); // Enums prefer niches close to the beginning or the end of the variants so that other // (smaller) data-carrying variants can be packed into the space after/before the niche. // If the default field ordering does not give us a niche at the front then we do a second @@ -296,7 +296,7 @@ impl LayoutCalculator { && (fields.len() > 1 && head_space != 0 && tail_space > 0) { let alt_layout = self - .univariant_biased(fields, repr, kind, NicheBias::End) + .layout_of_univariant_biased(fields, repr, kind, NicheBias::End) .expect("alt layout should always work"); let alt_niche = alt_layout .largest_niche @@ -540,7 +540,7 @@ impl LayoutCalculator { StructKind::MaybeUnsized }; - let mut st = self.univariant(&variants[v], repr, kind)?; + let mut st = self.layout_of_univariant(&variants[v], repr, kind)?; st.variants = Variants::Single { index: v }; if is_special_no_niche { @@ -608,7 +608,7 @@ impl LayoutCalculator { let mut variant_layouts = variants .iter() .map(|v| { - let st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?; + let st = self.layout_of_univariant(v, repr, StructKind::AlwaysSized).ok()?; variants_info.push(VariantLayoutInfo { align_abi: st.align.abi }); @@ -810,7 +810,7 @@ impl LayoutCalculator { let mut layout_variants = variants .iter() .map(|field_layouts| { - let st = self.univariant( + let st = self.layout_of_univariant( field_layouts, repr, StructKind::Prefixed(min_ity.size(), prefix_align), @@ -1073,7 +1073,7 @@ impl LayoutCalculator { Ok(best_layout) } - fn univariant_biased<'a, FieldIdx, VariantIdx, F>( + fn layout_of_univariant_biased<'a, FieldIdx, VariantIdx, F>( &self, fields: &IndexSlice, repr: &ReprOptions, diff --git a/compiler/rustc_abi/src/layout/coroutine.rs b/compiler/rustc_abi/src/layout/coroutine.rs index 4a1cb5bc86fc4..c102fbb3e843c 100644 --- a/compiler/rustc_abi/src/layout/coroutine.rs +++ b/compiler/rustc_abi/src/layout/coroutine.rs @@ -171,8 +171,11 @@ where let promoted_layouts = ineligible_locals.iter().map(|local| local_layouts[local]); prefix_layouts.push(tag_to_layout(tag)); prefix_layouts.extend(promoted_layouts); - let prefix = - calc.univariant(&prefix_layouts, &ReprOptions::default(), StructKind::AlwaysSized)?; + let prefix = calc.layout_of_univariant( + &prefix_layouts, + &ReprOptions::default(), + StructKind::AlwaysSized, + )?; let (prefix_size, prefix_align) = (prefix.size, prefix.align); @@ -225,7 +228,7 @@ where }) .map(|local| local_layouts[*local]); - let mut variant = calc.univariant( + let mut variant = calc.layout_of_univariant( &variant_only_tys.collect::>(), &ReprOptions::default(), StructKind::Prefixed(prefix_size, prefix_align.abi), diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index ef1241266cac6..ab9437abeb2ce 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -235,7 +235,7 @@ fn layout_of_uncached<'tcx>( let univariant = |tys: &[Ty<'tcx>], kind| { let fields = tys.iter().map(|ty| cx.layout_of(*ty)).try_collect::>()?; let repr = ReprOptions::default(); - map_layout(cx.calc.univariant(&fields, &repr, kind)) + map_layout(cx.calc.layout_of_univariant(&fields, &repr, kind)) }; debug_assert!(!ty.has_non_region_infer()); @@ -523,11 +523,11 @@ fn layout_of_uncached<'tcx>( .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; let element = cx.layout_of(element)?; - map_layout(cx.calc.array_like(&element, Some(count)))? + map_layout(cx.calc.layout_of_array_like(&element, Some(count)))? } ty::Slice(element) => { let element = cx.layout_of(element)?; - map_layout(cx.calc.array_like(&element, None).map(|mut layout| { + map_layout(cx.calc.layout_of_array_like(&element, None).map(|mut layout| { // a randomly chosen value to distinguish slices layout.randomization_seed = Hash64::new(0x2dcba99c39784102); layout @@ -535,7 +535,7 @@ fn layout_of_uncached<'tcx>( } ty::Str => { let element = scalar(Int(I8, false)); - map_layout(cx.calc.array_like(&element, None).map(|mut layout| { + map_layout(cx.calc.layout_of_array_like(&element, None).map(|mut layout| { // another random value layout.randomization_seed = Hash64::new(0xc1325f37d127be22); layout @@ -586,7 +586,7 @@ fn layout_of_uncached<'tcx>( let layout = cx .calc - .coroutine( + .layout_of_coroutine( &local_layouts, prefix_layouts, &info.variant_fields, @@ -638,7 +638,7 @@ fn layout_of_uncached<'tcx>( }; let element_layout = cx.layout_of(element_ty)?; - map_layout(cx.calc.scalable_vector_type( + map_layout(cx.calc.layout_of_scalable_vector_type( element_layout, element_count as u64, number_of_vectors, @@ -683,7 +683,7 @@ fn layout_of_uncached<'tcx>( } } - map_layout(cx.calc.simd_type(e_ly, e_len, def.repr().packed()))? + map_layout(cx.calc.layout_of_simd_type(e_ly, e_len, def.repr().packed()))? } // ADTs. From cbe62addd84f4563aa116c70b8b594939164bd07 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 27 Sep 2026 16:05:10 +0200 Subject: [PATCH 20/27] Remove #[linkage = "internal"] Rustc already defaults to this for regular items whenever possible. Overriding it would only lead to linker errors. And for depending on the exact codegen unit partitioning rustc uses, so there it is a bad idea to use it too. --- compiler/rustc_attr_parsing/src/attributes/link_attrs.rs | 3 --- compiler/rustc_span/src/symbol.rs | 1 - .../linkage-attr-does-not-panic-llvm-issue-33992.rs | 3 --- tests/ui/linkage-attr/linkage3.stderr | 2 +- tests/ui/lint/inline-exported.rs | 4 ---- 5 files changed, 1 insertion(+), 12 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index a00e5af00c2eb..1255cf34a94d4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -663,7 +663,6 @@ impl SingleAttributeParser for LinkageParser { "common", "extern_weak", "external", - "internal", "linkonce", "linkonce_odr", "weak", @@ -689,7 +688,6 @@ impl SingleAttributeParser for LinkageParser { sym::common => Linkage::Common, sym::extern_weak => Linkage::ExternalWeak, sym::external => Linkage::External, - sym::internal => Linkage::Internal, sym::linkonce => Linkage::LinkOnceAny, sym::linkonce_odr => Linkage::LinkOnceODR, sym::weak => Linkage::WeakAny, @@ -703,7 +701,6 @@ impl SingleAttributeParser for LinkageParser { sym::common, sym::extern_weak, sym::external, - sym::internal, sym::linkonce, sym::linkonce_odr, sym::weak, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e62ad42875248..8f87479d1e577 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1168,7 +1168,6 @@ symbols! { integer_max, integer_min, integral, - internal, internal_eq_trait_method_impls, internal_features, interrupt, diff --git a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs index 0717a2d5a6c9e..d80236cae7c6d 100644 --- a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs +++ b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs @@ -7,9 +7,6 @@ #[linkage = "external"] pub static TEST2: bool = true; -#[linkage = "internal"] -pub static TEST3: bool = true; - #[cfg(not(target_env = "msvc"))] #[linkage = "linkonce"] pub static TEST4: bool = true; diff --git a/tests/ui/linkage-attr/linkage3.stderr b/tests/ui/linkage-attr/linkage3.stderr index aa25cd39ba291..45555eef3d120 100644 --- a/tests/ui/linkage-attr/linkage3.stderr +++ b/tests/ui/linkage-attr/linkage3.stderr @@ -4,7 +4,7 @@ error[E0539]: malformed `linkage` attribute input LL | #[linkage = "foo"] | ^^^^^^^^^^----- | | - | valid arguments are `available_externally`, `common`, `extern_weak`, `external`, `internal`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` + | valid arguments are `available_externally`, `common`, `extern_weak`, `external`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` error: aborting due to 1 previous error diff --git a/tests/ui/lint/inline-exported.rs b/tests/ui/lint/inline-exported.rs index 6a23cd58236ff..bacbd128cd632 100644 --- a/tests/ui/lint/inline-exported.rs +++ b/tests/ui/lint/inline-exported.rs @@ -22,7 +22,3 @@ fn external_linkage() {} #[inline] fn normal() {} - -#[inline] -#[linkage = "internal"] // not exported -fn internal_linkage() {} From 94685f0f1f064084ba758b7ca1dc0bf9de10ef9b Mon Sep 17 00:00:00 2001 From: Asger Hautop Drewsen Date: Sun, 27 Sep 2026 15:57:22 +0000 Subject: [PATCH 21/27] Implement Default for NumBuffer * Implement Default for NumBuffer * Make insta-stable --- library/core/src/fmt/num_buffer.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/core/src/fmt/num_buffer.rs b/library/core/src/fmt/num_buffer.rs index 5c29964b1cff8..62089e30a06e2 100644 --- a/library/core/src/fmt/num_buffer.rs +++ b/library/core/src/fmt/num_buffer.rs @@ -84,3 +84,10 @@ impl NumBuffer { NumBuffer { buf: T::DEFAULT, phantom: core::marker::PhantomData } } } + +#[stable(feature = "num_buffer_default", since = "CURRENT_RUSTC_VERSION")] +impl Default for NumBuffer { + fn default() -> Self { + Self::new() + } +} From e2badb91b1535a53f612ebe2ba9a84e071f0b777 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 27 Sep 2026 16:08:18 +0200 Subject: [PATCH 22/27] Remove #[linkage = "external"] Rustc already defaults to this for #[no_mangle]/#[export_name] items. There is no reason to explicitly use it. --- .../rustc_attr_parsing/src/attributes/link_attrs.rs | 3 --- tests/codegen-llvm/direct-access-external-data.rs | 10 ---------- tests/run-make/linkage-attr-on-static/bar.rs | 2 +- .../run-make/naked-symbol-visibility/a_rust_dylib.rs | 11 ----------- tests/run-make/naked-symbol-visibility/rmake.rs | 3 --- tests/ui/asm/naked-functions/naked-functions.rs | 2 +- tests/ui/linkage-attr/auxiliary/def_external.rs | 5 ----- tests/ui/linkage-attr/auxiliary/def_weak.rs | 5 +++++ .../linkage-attr-does-not-panic-llvm-issue-33992.rs | 3 --- tests/ui/linkage-attr/linkage-import.rs | 6 +++--- tests/ui/linkage-attr/linkage3.stderr | 2 +- tests/ui/linkage-attr/linkage4.rs | 2 +- tests/ui/linkage-attr/linkage4.stderr | 2 +- tests/ui/lint/inline-exported.rs | 4 ++-- 14 files changed, 15 insertions(+), 45 deletions(-) delete mode 100644 tests/ui/linkage-attr/auxiliary/def_external.rs create mode 100644 tests/ui/linkage-attr/auxiliary/def_weak.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 1255cf34a94d4..8d57c75710a2a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -662,7 +662,6 @@ impl SingleAttributeParser for LinkageParser { "available_externally", "common", "extern_weak", - "external", "linkonce", "linkonce_odr", "weak", @@ -687,7 +686,6 @@ impl SingleAttributeParser for LinkageParser { sym::available_externally => Linkage::AvailableExternally, sym::common => Linkage::Common, sym::extern_weak => Linkage::ExternalWeak, - sym::external => Linkage::External, sym::linkonce => Linkage::LinkOnceAny, sym::linkonce_odr => Linkage::LinkOnceODR, sym::weak => Linkage::WeakAny, @@ -700,7 +698,6 @@ impl SingleAttributeParser for LinkageParser { sym::available_externally, sym::common, sym::extern_weak, - sym::external, sym::linkonce, sym::linkonce_odr, sym::weak, diff --git a/tests/codegen-llvm/direct-access-external-data.rs b/tests/codegen-llvm/direct-access-external-data.rs index a151bb6012e1e..43ee290d3a058 100644 --- a/tests/codegen-llvm/direct-access-external-data.rs +++ b/tests/codegen-llvm/direct-access-external-data.rs @@ -22,15 +22,6 @@ unsafe extern "C" { // When "linkage" is used, we generate an indirection global. // Check dso_local is still applied to the actual global. - // CHECK: @EXTERNAL = external - // DEFAULT-SAME: dso_local - // PIE-NOT: dso_local - // DIRECT-SAME: dso_local - // INDIRECT-NOT: dso_local - // CHECK-SAME: global i8 - #[linkage = "external"] - safe static EXTERNAL: *const u32; - // CHECK: @WEAK = extern_weak // DEFAULT-SAME: dso_local // PIE-NOT: dso_local @@ -44,6 +35,5 @@ unsafe extern "C" { #[no_mangle] pub fn refer() { core::hint::black_box(VAR); - core::hint::black_box(EXTERNAL); core::hint::black_box(WEAK); } diff --git a/tests/run-make/linkage-attr-on-static/bar.rs b/tests/run-make/linkage-attr-on-static/bar.rs index 68607cbb65721..cbab76ba6f403 100644 --- a/tests/run-make/linkage-attr-on-static/bar.rs +++ b/tests/run-make/linkage-attr-on-static/bar.rs @@ -1,7 +1,7 @@ #![feature(linkage)] #[no_mangle] -#[linkage = "external"] +#[linkage = "weak"] static BAZ: i32 = 21; #[link(name = "foo", kind = "static")] diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs index ce787f83ade6d..8d699a665ae24 100644 --- a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -51,17 +51,6 @@ pub extern "C" fn public_naked_generic() -> u32 { naked_asm!("mov rax, {}", "ret", const T::COUNT) } -#[linkage = "external"] -extern "C" fn vanilla_external_linkage() -> u32 { - 42 -} - -#[unsafe(naked)] -#[linkage = "external"] -extern "C" fn naked_external_linkage() -> u32 { - naked_asm!("mov rax, 42", "ret") -} - #[cfg(not(windows))] #[linkage = "weak"] extern "C" fn vanilla_weak_linkage() -> u32 { diff --git a/tests/run-make/naked-symbol-visibility/rmake.rs b/tests/run-make/naked-symbol-visibility/rmake.rs index 69317c872f895..1acbe11dc4ae4 100644 --- a/tests/run-make/naked-symbol-visibility/rmake.rs +++ b/tests/run-make/naked-symbol-visibility/rmake.rs @@ -29,9 +29,6 @@ fn main() { // -Zshare-generics. global_function(&rdylib, "public_naked_generic"); - global_function(&rdylib, "vanilla_external_linkage"); - global_function(&rdylib, "naked_external_linkage"); - // FIXME: make this work on windows (gnu and msvc). See the PR // https://github.com/rust-lang/rust/pull/128362 for some approaches // that don't work diff --git a/tests/ui/asm/naked-functions/naked-functions.rs b/tests/ui/asm/naked-functions/naked-functions.rs index d994b1151514a..e7da65a169074 100644 --- a/tests/ui/asm/naked-functions/naked-functions.rs +++ b/tests/ui/asm/naked-functions/naked-functions.rs @@ -224,7 +224,7 @@ pub extern "C" fn compatible_doc_attributes() { naked_asm!("", options(raw)); } -#[linkage = "external"] +#[linkage = "weak"] #[unsafe(naked)] pub extern "C" fn compatible_linkage() { naked_asm!("", options(raw)); diff --git a/tests/ui/linkage-attr/auxiliary/def_external.rs b/tests/ui/linkage-attr/auxiliary/def_external.rs deleted file mode 100644 index 2300930e513af..0000000000000 --- a/tests/ui/linkage-attr/auxiliary/def_external.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![feature(linkage)] -#![crate_type = "lib"] - -#[linkage="external"] -pub static EXTERN: u32 = 0; diff --git a/tests/ui/linkage-attr/auxiliary/def_weak.rs b/tests/ui/linkage-attr/auxiliary/def_weak.rs new file mode 100644 index 0000000000000..a8940f73c5536 --- /dev/null +++ b/tests/ui/linkage-attr/auxiliary/def_weak.rs @@ -0,0 +1,5 @@ +#![feature(linkage)] +#![crate_type = "lib"] + +#[linkage = "weak"] +pub static WEAK: u32 = 0; diff --git a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs index d80236cae7c6d..a7209b71c7809 100644 --- a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs +++ b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs @@ -4,9 +4,6 @@ #![feature(linkage)] -#[linkage = "external"] -pub static TEST2: bool = true; - #[cfg(not(target_env = "msvc"))] #[linkage = "linkonce"] pub static TEST4: bool = true; diff --git a/tests/ui/linkage-attr/linkage-import.rs b/tests/ui/linkage-attr/linkage-import.rs index 838d1fc29a238..51fcf1c133f32 100644 --- a/tests/ui/linkage-attr/linkage-import.rs +++ b/tests/ui/linkage-attr/linkage-import.rs @@ -1,8 +1,8 @@ //@ build-pass -//@ aux-build:def_external.rs +//@ aux-build:def_weak.rs -extern crate def_external as dep; +extern crate def_weak as dep; fn main() { - println!("{:p}", &dep::EXTERN); + println!("{:p}", &dep::WEAK); } diff --git a/tests/ui/linkage-attr/linkage3.stderr b/tests/ui/linkage-attr/linkage3.stderr index 45555eef3d120..4a85c39009517 100644 --- a/tests/ui/linkage-attr/linkage3.stderr +++ b/tests/ui/linkage-attr/linkage3.stderr @@ -4,7 +4,7 @@ error[E0539]: malformed `linkage` attribute input LL | #[linkage = "foo"] | ^^^^^^^^^^----- | | - | valid arguments are `available_externally`, `common`, `extern_weak`, `external`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` + | valid arguments are `available_externally`, `common`, `extern_weak`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` error: aborting due to 1 previous error diff --git a/tests/ui/linkage-attr/linkage4.rs b/tests/ui/linkage-attr/linkage4.rs index 3b935f9723dc1..25df81189d8f5 100644 --- a/tests/ui/linkage-attr/linkage4.rs +++ b/tests/ui/linkage-attr/linkage4.rs @@ -1,4 +1,4 @@ -#[linkage = "external"] +#[linkage = "weak"] static foo: isize = 0; //~^^ ERROR: the `linkage` attribute is experimental and not portable diff --git a/tests/ui/linkage-attr/linkage4.stderr b/tests/ui/linkage-attr/linkage4.stderr index a5970d100581a..2f04ceee5199d 100644 --- a/tests/ui/linkage-attr/linkage4.stderr +++ b/tests/ui/linkage-attr/linkage4.stderr @@ -1,7 +1,7 @@ error[E0658]: the `linkage` attribute is experimental and not portable across platforms --> $DIR/linkage4.rs:1:3 | -LL | #[linkage = "external"] +LL | #[linkage = "weak"] | ^^^^^^^ | = note: see issue #29603 for more information diff --git a/tests/ui/lint/inline-exported.rs b/tests/ui/lint/inline-exported.rs index bacbd128cd632..25ca5f0633079 100644 --- a/tests/ui/lint/inline-exported.rs +++ b/tests/ui/lint/inline-exported.rs @@ -17,8 +17,8 @@ fn export_name() {} #[inline] //~^ ERROR: `#[inline]` is ignored on externally exported functions -#[linkage = "external"] -fn external_linkage() {} +#[linkage = "weak"] +fn weak_linkage() {} #[inline] fn normal() {} From cd52149e26b190e3fbfebf46b0d97c63828c73e0 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 27 Sep 2026 16:20:51 +0200 Subject: [PATCH 23/27] Remove #[linkage = "common"] A common definition is like a weak definition except that it must be a zero-initialized static and when merging two common symbols with the same name, the size and alignment are set to the higher of both symbols. This is used for tentative definitions in C and doesn't have any reason to exist outside of that. This behavior doesn't work across dylibs and common symbols have inconsistent behavior across linkers [1]. It is also fragile to rely on getting the largest size of all common symbol definitions as a (possibly smaller) global definition can override it. Link: https://maskray.me/blog/all-about-common-symbols [1] --- compiler/rustc_attr_ir/src/data_structures.rs | 1 - .../src/attributes/link_attrs.rs | 3 --- compiler/rustc_codegen_gcc/src/base.rs | 19 ++++------------- compiler/rustc_codegen_gcc/src/consts.rs | 21 ++----------------- compiler/rustc_codegen_llvm/src/base.rs | 1 - .../rustc_codegen_ssa/src/mir/naked_asm.rs | 1 - .../rustc_monomorphize/src/partitioning.rs | 1 - compiler/rustc_span/src/symbol.rs | 1 - .../codegen_attr_on_required_trait_method.rs | 2 +- ...degen_attr_on_required_trait_method.stderr | 2 +- .../common-linkage-non-zero-init.rs | 17 --------------- .../common-linkage-non-zero-init.stderr | 3 --- ...ge-attr-does-not-panic-llvm-issue-33992.rs | 1 - tests/ui/linkage-attr/linkage3.stderr | 2 +- 14 files changed, 9 insertions(+), 66 deletions(-) delete mode 100644 tests/ui/linkage-attr/common-linkage-non-zero-init.rs delete mode 100644 tests/ui/linkage-attr/common-linkage-non-zero-init.stderr diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index 712ed41ae7759..55c0bedb9c39c 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -283,7 +283,6 @@ impl StrippedCfgItem { #[derive(StableHash, PrintAttribute)] pub enum Linkage { AvailableExternally, - Common, ExternalWeak, External, Internal, diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 8d57c75710a2a..6b4c5b8a681ca 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -660,7 +660,6 @@ impl SingleAttributeParser for LinkageParser { ]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: [ "available_externally", - "common", "extern_weak", "linkonce", "linkonce_odr", @@ -684,7 +683,6 @@ impl SingleAttributeParser for LinkageParser { // and don't have to be, LLVM treats them as no-ops. let linkage = match value { sym::available_externally => Linkage::AvailableExternally, - sym::common => Linkage::Common, sym::extern_weak => Linkage::ExternalWeak, sym::linkonce => Linkage::LinkOnceAny, sym::linkonce_odr => Linkage::LinkOnceODR, @@ -696,7 +694,6 @@ impl SingleAttributeParser for LinkageParser { name_value.value_span, &[ sym::available_externally, - sym::common, sym::extern_weak, sym::linkonce, sym::linkonce_odr, diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 0dd46558ed796..76eb16c9cc32b 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -56,19 +56,14 @@ pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR - | Linkage::ExternalWeak - | Linkage::Common => GlobalKind::Exported, + | Linkage::ExternalWeak => GlobalKind::Exported, } } /// The attribute a global *definition* needs on top of its [`GlobalKind`] to get this linkage. #[cfg(feature = "master")] pub fn global_linkage_attribute<'gcc>(linkage: Linkage) -> Option> { - match linkage { - Linkage::Common => Some(VarAttribute::Common), - _ if linkage_needs_weak_attribute(linkage) => Some(VarAttribute::Weak), - _ => None, - } + if linkage_needs_weak_attribute(linkage) { Some(VarAttribute::Weak) } else { None } } /// The type of a function *definition* with an explicit `#[linkage]`. @@ -86,17 +81,12 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR - | Linkage::ExternalWeak - | Linkage::Common => FunctionType::Exported, + | Linkage::ExternalWeak => FunctionType::Exported, } } /// Whether a definition with this linkage must carry the `weak` attribute, so that a strong /// definition in another object file wins over it instead of clashing with it. -/// -/// `common` is in here for functions only: GCC honours that attribute on a variable, but drops it -/// on a function, so a common function falls back to weak. Globals go through -/// `global_linkage_attribute` instead. #[cfg(feature = "master")] pub fn linkage_needs_weak_attribute(linkage: Linkage) -> bool { match linkage { @@ -104,8 +94,7 @@ pub fn linkage_needs_weak_attribute(linkage: Linkage) -> bool { | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR - | Linkage::ExternalWeak - | Linkage::Common => true, + | Linkage::ExternalWeak => true, Linkage::External | Linkage::AvailableExternally | Linkage::Internal => false, } } diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 8576dfe079163..618fc4f983626 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -13,8 +13,7 @@ use rustc_hir::def_id::LOCAL_CRATE; use rustc_log::tracing::trace; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ - self, Allocation, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, - read_target_uint, + self, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, read_target_uint, }; use rustc_middle::mono::MonoItem; use rustc_middle::ty::layout::LayoutOf; @@ -113,12 +112,7 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { // NOTE: Alignment from attributes has already been applied to the allocation. set_global_alignment(self, global, alloc.align); - // A common symbol is storage the linker allocates and zero-fills, so giving the definition - // an initializer — even an all-zero one — takes it back out of `.comm`. A non-zero one is - // kept: the symbol is then an ordinary definition, which is what GCC does with it too. - if attrs.linkage != Some(Linkage::Common) || !is_zero_initializer(alloc) { - global.global_set_initializer_rvalue(value); - } + global.global_set_initializer_rvalue(value); // As an optimization, all shared statics which do not have interior // mutability are placed into read-only memory. @@ -458,17 +452,6 @@ pub(crate) fn const_alloc_to_gcc_uncached<'gcc>( cx.const_struct(&llvals, true) } -/// Whether this allocation is all zeroes, and so needs no initializer to be spelled out. -fn is_zero_initializer(alloc: &Allocation) -> bool { - alloc.provenance().ptrs().is_empty() - // This `inspect` is okay: it is within the bounds of the allocation, there is no provenance - // to misread, and it does not affect interpreter execution. - && alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.size().bytes_usize()) - .iter() - .all(|&byte| byte == 0) -} - fn codegen_static_initializer<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, def_id: DefId, diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 6e7c31f7743d7..d65cf652844ed 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -237,7 +237,6 @@ pub(crate) fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, Linkage::Internal => llvm::Linkage::InternalLinkage, Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, - Linkage::Common => llvm::Linkage::CommonLinkage, } } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 3162463d222ee..fcaadb21667f6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -228,7 +228,6 @@ fn prefix_and_suffix<'tcx>( // LTO can fail when internal linkage is used. emit_fatal("naked functions may not have internal linkage") } - Linkage::Common => emit_fatal("Functions may not have common linkage"), Linkage::AvailableExternally => { // this would make the function equal an extern definition emit_fatal("Functions may not have available_externally linkage") diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 43ce9634cc203..16df895b0f728 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1252,7 +1252,6 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio Linkage::WeakODR => "WeakODR", Linkage::Internal => "Internal", Linkage::ExternalWeak => "ExternalWeak", - Linkage::Common => "Common", }; output.push('['); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8f87479d1e577..b7aa25a5cfa7c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -666,7 +666,6 @@ symbols! { cold_path, collapse_debuginfo, column, - common, compare_bytes, compare_exchange, compare_exchange_weak, diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.rs b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs index 737ebf7f2b144..2ea4e0b5a395c 100644 --- a/tests/ui/attributes/codegen_attr_on_required_trait_method.rs +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs @@ -11,7 +11,7 @@ trait Test { //~^ ERROR cannot be used on required trait methods [unused_attributes] //~| WARN previously accepted fn method2(&self); - #[linkage = "common"] + #[linkage = "weak"] //~^ ERROR `linkage` attribute cannot be used on required trait methods fn method3(&self); #[track_caller] diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr index b5452ba2882c0..729231956337d 100644 --- a/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr @@ -1,7 +1,7 @@ error: the `linkage` attribute cannot be used on required trait methods --> $DIR/codegen_attr_on_required_trait_method.rs:14:7 | -LL | #[linkage = "common"] +LL | #[linkage = "weak"] | ^^^^^^^ | = help: the `linkage` attribute can be applied to foreign functions, foreign statics, functions with a body, and statics diff --git a/tests/ui/linkage-attr/common-linkage-non-zero-init.rs b/tests/ui/linkage-attr/common-linkage-non-zero-init.rs deleted file mode 100644 index 512616251c2f7..0000000000000 --- a/tests/ui/linkage-attr/common-linkage-non-zero-init.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ build-fail -//@ failure-status: 101 -//@ known-bug: #109681 -//@ ignore-wasm32 this appears to SIGABRT on wasm, not fail cleanly -//@ compile-flags: -Z verify-llvm-ir -//@ ignore-backends: gcc - -// This test verifies that we continue to hit the LLVM error for common linkage with non-zero -// initializers, since it generates invalid LLVM IR. -// Linkages are internal features marked as perma-unstable, so we don't need to fix the issue -// for now. -#![crate_type="lib"] -#![feature(linkage)] - -#[linkage = "common"] -#[no_mangle] -pub static TEST: bool = true; diff --git a/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr b/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr deleted file mode 100644 index 93015bb2bac12..0000000000000 --- a/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr +++ /dev/null @@ -1,3 +0,0 @@ -'common' global must have a zero initializer! -ptr @TEST -rustc-LLVM ERROR: Broken module found, compilation aborted! diff --git a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs index a7209b71c7809..eb8a1a71ed17f 100644 --- a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs +++ b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs @@ -1,6 +1,5 @@ //@ run-pass //@ ignore-apple -//@ ignore-wasm32 common linkage not implemented right now #![feature(linkage)] diff --git a/tests/ui/linkage-attr/linkage3.stderr b/tests/ui/linkage-attr/linkage3.stderr index 4a85c39009517..616f80df09c8b 100644 --- a/tests/ui/linkage-attr/linkage3.stderr +++ b/tests/ui/linkage-attr/linkage3.stderr @@ -4,7 +4,7 @@ error[E0539]: malformed `linkage` attribute input LL | #[linkage = "foo"] | ^^^^^^^^^^----- | | - | valid arguments are `available_externally`, `common`, `extern_weak`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` + | valid arguments are `available_externally`, `extern_weak`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` error: aborting due to 1 previous error From 37c0ed5ce88fedc68b0e120754e7968ad6646a87 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 26 Sep 2026 18:46:11 +0200 Subject: [PATCH 24/27] Add docs --- compiler/rustc_abi/src/layout.rs | 13 +++++++++---- compiler/rustc_abi/src/lib.rs | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index a10548d1a6059..3234ae0bbe6b9 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -263,6 +263,11 @@ impl LayoutCalculator { ) } + /// Compute the layout for a univariant (see [`Variants::Single`]). + /// + /// As a consumer of `rustc_abi`, you should only use this method for non-ADTs. + /// For structs and univariant enums, use [`Self::layout_of_struct`] instead + /// (it uses this function internally). pub fn layout_of_univariant<'a, FieldIdx, VariantIdx, F>( &self, fields: &IndexSlice, @@ -514,7 +519,10 @@ impl LayoutCalculator { }) } - /// single-variant enums are just structs, if you think about it + /// Calculate the layout for a struct, or a single-variant enum. + /// + /// They are the same thing, if you think about it + /// (Typechecking will reject discriminant-sizing attrs.) fn layout_of_struct<'a, FieldIdx, VariantIdx, F>( &self, repr: &ReprOptions, @@ -529,9 +537,6 @@ impl LayoutCalculator { VariantIdx: Idx, F: Deref> + fmt::Debug + Copy, { - // Struct, or univariant enum equivalent to a struct. - // (Typechecking will reject discriminant-sizing attrs.) - let dl = self.cx.data_layout(); let v = present_first; let kind = if is_enum || variants[v].is_empty() || always_sized { diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 56cfbf138af00..82225d580d414 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -2027,10 +2027,10 @@ pub enum Variants { /// 2. the never type Empty, - /// The type has a single valid variant. + /// The type has a single valid variant. Such types are called "univariant". /// /// This is the case for: - /// 1. enums with a single inhabited variant + /// 1. enums with a single inhabited variant, aka. "univariant enums" /// 2. structs, unions, and non-ADTs (except coroutines; see below), /// as those can't have multiple variants Single { From 0858ecdbca0ac1ebc1ea9f81068069dc5fe7032e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 26 Sep 2026 18:46:11 +0200 Subject: [PATCH 25/27] `layout_of_struct`: rename the `present_first` param The name makes some sense at the callsite, but inside the function, the parameter refers to the `VariantIdx` that should be use for the newly-created layout, so name accordingly. Also move closer to the related `variants` param (Note: could consider passing `variants[present_first]` instead of `variants`, as that's the only way `variants` is used -- at the same type, passing `variants` seems more consistent with the rest of the file.) --- compiler/rustc_abi/src/layout.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 3234ae0bbe6b9..e099290438080 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -378,10 +378,10 @@ impl LayoutCalculator { self.layout_of_struct( repr, variants, + present_first, is_enum, is_special_no_niche, always_sized, - present_first, ) } else { // At this point, we have handled all unions and @@ -527,10 +527,10 @@ impl LayoutCalculator { &self, repr: &ReprOptions, variants: &IndexSlice>, + variant_idx: VariantIdx, is_enum: bool, is_special_no_niche: bool, always_sized: bool, - present_first: VariantIdx, ) -> LayoutCalculatorResult where FieldIdx: Idx, @@ -538,7 +538,7 @@ impl LayoutCalculator { F: Deref> + fmt::Debug + Copy, { let dl = self.cx.data_layout(); - let v = present_first; + let v = variant_idx; let kind = if is_enum || variants[v].is_empty() || always_sized { StructKind::AlwaysSized } else { From 6668485ad973381aa256560c237e98ae334d27ee Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 26 Sep 2026 19:35:19 +0200 Subject: [PATCH 26/27] introduce `NicheOptimizations` This is a more type-safe alternative for the `niche_optimizations: bool` param --- compiler/rustc_abi/src/layout.rs | 13 +++++++------ compiler/rustc_abi/src/lib.rs | 12 ++++++++++++ compiler/rustc_ty_utils/src/layout.rs | 13 ++++++++----- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index e099290438080..fa03fb87d02d1 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -11,8 +11,9 @@ use tracing::{debug, trace}; use crate::{ AbiAlign, Align, BackendLaneCount, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, - IndexVec, Integer, LayoutData, Niche, NumScalableVectors, Primitive, ReprOptions, Scalar, Size, - StructKind, TagEncoding, TargetDataLayout, VariantLayout, Variants, WrappingRange, + IndexVec, Integer, LayoutData, Niche, NicheOptimizations, NumScalableVectors, Primitive, + ReprOptions, Scalar, Size, StructKind, TagEncoding, TargetDataLayout, VariantLayout, Variants, + WrappingRange, }; mod coroutine; @@ -343,7 +344,7 @@ impl LayoutCalculator { repr: &ReprOptions, variants: &IndexSlice>, is_enum: bool, - is_special_no_niche: bool, + niche_optimizations: NicheOptimizations, discr_range_of_repr: impl Fn(RangeFrom, RangeToInclusive) -> (Integer, bool), discriminants: impl Iterator, always_sized: bool, @@ -380,7 +381,7 @@ impl LayoutCalculator { variants, present_first, is_enum, - is_special_no_niche, + niche_optimizations, always_sized, ) } else { @@ -529,7 +530,7 @@ impl LayoutCalculator { variants: &IndexSlice>, variant_idx: VariantIdx, is_enum: bool, - is_special_no_niche: bool, + niche_optimizations: NicheOptimizations, always_sized: bool, ) -> LayoutCalculatorResult where @@ -548,7 +549,7 @@ impl LayoutCalculator { let mut st = self.layout_of_univariant(&variants[v], repr, kind)?; st.variants = Variants::Single { index: v }; - if is_special_no_niche { + if niche_optimizations == NicheOptimizations::Disabled { let hide_niches = |scalar: &mut _| match scalar { Scalar::Initialized { value, valid_range } => { *valid_range = WrappingRange::full(value.size(dl)) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 82225d580d414..ef34addace196 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -2195,6 +2195,18 @@ impl Niche { } } +/// Whether niche optimizations should be performed during layout calculation. +/// +/// [`UnsafeCell`] and [`UnsafePinned`] both disable niche optimizations. +/// +/// [`UnsafeCell`]: std::cell::UnsafeCell +/// [`UnsafePinned`]: std::pin::UnsafePinned +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum NicheOptimizations { + Enabled, + Disabled, +} + // NOTE: This struct is generic over the FieldIdx and VariantIdx for rust-analyzer usage. #[derive(PartialEq, Eq, Hash, Clone)] #[cfg_attr(feature = "nightly", derive(StableHash))] diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index ab9437abeb2ce..1522ecbb55de9 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -5,8 +5,8 @@ use rustc_abi::Integer::{I8, I32}; use rustc_abi::Primitive::{self, Float, Int, Pointer}; use rustc_abi::{ AddressSpace, BackendRepr, FIRST_VARIANT, FieldIdx, FieldsShape, HasDataLayout, Layout, - LayoutCalculatorError, LayoutData, Niche, ReprOptions, Scalar, Size, StructKind, TagEncoding, - VariantIdx, Variants, WrappingRange, + LayoutCalculatorError, LayoutData, Niche, NicheOptimizations, ReprOptions, Scalar, Size, + StructKind, TagEncoding, VariantIdx, Variants, WrappingRange, }; use rustc_attr_ir::find_attr; use rustc_attr_ir::lang_items::LangItem; @@ -713,7 +713,10 @@ fn layout_of_uncached<'tcx>( } // UnsafeCell and UnsafePinned both disable niche optimizations - let is_special_no_niche = def.is_unsafe_cell() || def.is_unsafe_pinned(); + let niche_optimizations = match def.is_unsafe_cell() || def.is_unsafe_pinned() { + true => NicheOptimizations::Disabled, + false => NicheOptimizations::Enabled, + }; let discr_range_of_repr = |min: RangeFrom, max: RangeToInclusive| { abi::Integer::discr_range_of_repr(tcx, ty, &def.repr(), min.start, max.last) @@ -743,7 +746,7 @@ fn layout_of_uncached<'tcx>( &def.repr(), &variants, def.is_enum(), - is_special_no_niche, + niche_optimizations, discr_range_of_repr, discriminants_iter(), !maybe_unsized, @@ -772,7 +775,7 @@ fn layout_of_uncached<'tcx>( &def.repr(), &variants, def.is_enum(), - is_special_no_niche, + niche_optimizations, discr_range_of_repr, discriminants_iter(), !maybe_unsized, From dfa855cfca4f4e923916e7c0483042bf0d050697 Mon Sep 17 00:00:00 2001 From: Matilde Morrone Date: Sun, 27 Sep 2026 21:48:49 +0200 Subject: [PATCH 27/27] mailmap: add Matilde Morrone --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index 32fbff098127d..62eadbcd29176 100644 --- a/.mailmap +++ b/.mailmap @@ -459,6 +459,8 @@ Mateusz Mikuła Mateusz Mikuła Mateusz Mikuła Mateusz Mikuła +Matilde Morrone +Matilde Morrone Matt Brubeck Matthew Auld Matthew Jasper