From 686ddd3e2e4df0ee4b0dc332f69aff58a24a0de0 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 16 Mar 2026 06:12:15 +0100 Subject: [PATCH 01/33] tests/debuginfo/basic-stepping.rs: Explain why all lines are not steppable Some optimization passes _improve_ compile times [1]. So we want to run some passes even with `-Copt-level=0`. That means that some of the lines in the test can be optimized away. To make regression testing more robust, we also want to run the test with such passes disabled. The solution is to use two revisions. One with default `-Copt-level=0` passes, and one "even less optimized", with enough optimization passes disabled to keep the maximum number of lines steppable. [1]: https://github.com/rust-lang/compiler-team/issues/319 --- tests/debuginfo/basic-stepping.rs | 59 ++++++++++++++++++------------- tests/debuginfo/macro-stepping.rs | 8 ++--- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/tests/debuginfo/basic-stepping.rs b/tests/debuginfo/basic-stepping.rs index 238ab12c186ef..a4410c70ba38a 100644 --- a/tests/debuginfo/basic-stepping.rs +++ b/tests/debuginfo/basic-stepping.rs @@ -9,9 +9,20 @@ // Debugger tests need debuginfo //@ compile-flags: -g -// FIXME(#128945): SingleUseConsts shouldn't need to be disabled. -//@ revisions: default-mir-passes no-SingleUseConsts-mir-pass -//@ [no-SingleUseConsts-mir-pass] compile-flags: -Zmir-enable-passes=-SingleUseConsts +// Some optimization passes _improve_ compile times [1]. So we want to run some +// passes even with `-Copt-level=0`. That means that some of the lines below can +// be optimized away. To make regression testing more robust, we also want to +// run this test with such passes disabled. The solution is to use two +// revisions. One with default `-Copt-level=0` passes, and one "even less +// optimized", with enough optimization passes disabled to keep the maximum +// number of lines steppable. +// +// If `-Zmir-enable-passes=-...` ends up being annoying to maintain, we can try +// switching to `-Zmir-opt-level=0` instead. +// +// [1]: https://github.com/rust-lang/compiler-team/issues/319 +//@ revisions: opt-level-0 maximally-steppable +//@ [maximally-steppable] compile-flags: -Zmir-enable-passes=-SingleUseConsts // === GDB TESTS =================================================================================== @@ -20,12 +31,12 @@ //@ gdb-command: next //@ gdb-check: let d = c = 99; //@ gdb-command: next -//@ [no-SingleUseConsts-mir-pass] gdb-check: let e = "hi bob"; -//@ [no-SingleUseConsts-mir-pass] gdb-command: next -//@ [no-SingleUseConsts-mir-pass] gdb-check: let f = b"hi bob"; -//@ [no-SingleUseConsts-mir-pass] gdb-command: next -//@ [no-SingleUseConsts-mir-pass] gdb-check: let g = b'9'; -//@ [no-SingleUseConsts-mir-pass] gdb-command: next +//@ [maximally-steppable] gdb-check: let e = "hi bob"; +//@ [maximally-steppable] gdb-command: next +//@ [maximally-steppable] gdb-check: let f = b"hi bob"; +//@ [maximally-steppable] gdb-command: next +//@ [maximally-steppable] gdb-check: let g = b'9'; +//@ [maximally-steppable] gdb-command: next //@ gdb-check: let h = ["whatever"; 8]; //@ gdb-command: next //@ gdb-check: let i = [1,2,3,4]; @@ -61,15 +72,15 @@ //@ lldb-check: [...]let d = c = 99;[...] //@ lldb-command: next //@ lldb-command: frame select -//@ [no-SingleUseConsts-mir-pass] lldb-check: [...]let e = "hi bob";[...] -//@ [no-SingleUseConsts-mir-pass] lldb-command: next -//@ [no-SingleUseConsts-mir-pass] lldb-command: frame select -//@ [no-SingleUseConsts-mir-pass] lldb-check: [...]let f = b"hi bob";[...] -//@ [no-SingleUseConsts-mir-pass] lldb-command: next -//@ [no-SingleUseConsts-mir-pass] lldb-command: frame select -//@ [no-SingleUseConsts-mir-pass] lldb-check: [...]let g = b'9';[...] -//@ [no-SingleUseConsts-mir-pass] lldb-command: next -//@ [no-SingleUseConsts-mir-pass] lldb-command: frame select +//@ [maximally-steppable] lldb-check: [...]let e = "hi bob";[...] +//@ [maximally-steppable] lldb-command: next +//@ [maximally-steppable] lldb-command: frame select +//@ [maximally-steppable] lldb-check: [...]let f = b"hi bob";[...] +//@ [maximally-steppable] lldb-command: next +//@ [maximally-steppable] lldb-command: frame select +//@ [maximally-steppable] lldb-check: [...]let g = b'9';[...] +//@ [maximally-steppable] lldb-command: next +//@ [maximally-steppable] lldb-command: frame select //@ lldb-check: [...]let h = ["whatever"; 8];[...] //@ lldb-command: next //@ lldb-command: frame select @@ -107,12 +118,12 @@ //@ cdb-check: [...]: let mut c = 27; //@ cdb-command: p //@ cdb-check: [...]: let d = c = 99; -//@ [no-SingleUseConsts-mir-pass] cdb-command: p -//@ [no-SingleUseConsts-mir-pass] cdb-check: [...]: let e = "hi bob"; -//@ [no-SingleUseConsts-mir-pass] cdb-command: p -//@ [no-SingleUseConsts-mir-pass] cdb-check: [...]: let f = b"hi bob"; -//@ [no-SingleUseConsts-mir-pass] cdb-command: p -//@ [no-SingleUseConsts-mir-pass] cdb-check: [...]: let g = b'9'; +//@ [maximally-steppable] cdb-command: p +//@ [maximally-steppable] cdb-check: [...]: let e = "hi bob"; +//@ [maximally-steppable] cdb-command: p +//@ [maximally-steppable] cdb-check: [...]: let f = b"hi bob"; +//@ [maximally-steppable] cdb-command: p +//@ [maximally-steppable] cdb-check: [...]: let g = b'9'; //@ cdb-command: p //@ cdb-check: [...]: let h = ["whatever"; 8]; //@ cdb-command: p diff --git a/tests/debuginfo/macro-stepping.rs b/tests/debuginfo/macro-stepping.rs index 3f57eb9ad79bd..c2f6183e3c7b0 100644 --- a/tests/debuginfo/macro-stepping.rs +++ b/tests/debuginfo/macro-stepping.rs @@ -16,9 +16,9 @@ extern crate macro_stepping; // exports new_scope!() //@ compile-flags: -g -// FIXME(#128945): SingleUseConsts shouldn't need to be disabled. -//@ revisions: default-mir-passes no-SingleUseConsts-mir-pass -//@ [no-SingleUseConsts-mir-pass] compile-flags: -Zmir-enable-passes=-SingleUseConsts +// See explanation in `tests/debuginfo/basic-stepping.rs`. +//@ revisions: opt-level-0 maximally-steppable +//@ [maximally-steppable] compile-flags: -Zmir-enable-passes=-SingleUseConsts // === GDB TESTS =================================================================================== @@ -51,7 +51,7 @@ extern crate macro_stepping; // exports new_scope!() //@ gdb-check:[...]#inc-loc2[...] //@ gdb-command:next //@ gdb-command:frame -//@ [no-SingleUseConsts-mir-pass] gdb-check:[...]#inc-loc3[...] +//@ [maximally-steppable] gdb-check:[...]#inc-loc3[...] // === LLDB TESTS ================================================================================== From c6cb2cc851ea1e39f9bd2390dedfca49bc761bd1 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 25 Mar 2026 19:05:06 +0300 Subject: [PATCH 02/33] resolve: Rename `NameResolution::binding` to `determined_decl` Avoid two of its uses in diagnostic reporting and effective visibility collection. The method is only supposed to be used inside the import resolution algorithm, and the removed uses happened after import resolution already converged. Some comments are also added. --- compiler/rustc_resolve/src/diagnostics.rs | 2 +- .../src/effective_visibilities.rs | 2 +- compiler/rustc_resolve/src/imports.rs | 19 +++++++++++++------ compiler/rustc_resolve/src/lib.rs | 8 +++++--- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 97c88064e9799..6e1e5270d0e00 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2961,7 +2961,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let binding_key = BindingKey::new(IdentKey::new(ident), MacroNS); - let binding = self.resolution(crate_module, binding_key)?.binding()?; + let binding = self.resolution(crate_module, binding_key)?.best_decl()?; let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { return None; }; diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 55518276a4f0f..cd7790f27b5f5 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -114,7 +114,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) { let module = self.r.expect_module(module_id.to_def_id()); for (_, name_resolution) in self.r.resolutions(module).borrow().iter() { - let Some(mut decl) = name_resolution.borrow().binding() else { + let Some(mut decl) = name_resolution.borrow().best_decl() else { continue; }; // Set the given effective visibility level to `Level::Direct` and diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 6507ee3477379..3103ca32cc0eb 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -256,8 +256,15 @@ impl<'ra> NameResolution<'ra> { NameResolution { single_imports: FxIndexSet::default(), orig_ident_span, .. } } - /// Returns the binding for the name if it is known or None if it not known. - pub(crate) fn binding(&self) -> Option> { + /// Returns the best declaration if it is not going to change, and `None` if the best + /// declaration may still change to something else. + /// FIXME: this function considers `single_imports`, but not `unexpanded_invocations`, so + /// the returned declaration may actually change after expanding macros in the same module, + /// because of this fact we have glob overwriting (`select_glob_decl`). Consider using + /// `unexpanded_invocations` here and avoiding glob overwriting entirely, if it doesn't cause + /// code breakage in practice. + /// FIXME: relationship between this function and similar `DeclData::determined` is unclear. + pub(crate) fn determined_decl(&self) -> Option> { self.best_decl().and_then(|binding| { if !binding.is_glob_import() || self.single_imports.is_empty() { Some(binding) @@ -509,11 +516,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let resolution = &mut *self .resolution_or_default(module, key, orig_ident_span) .borrow_mut_unchecked(); - let old_decl = resolution.binding(); + let old_decl = resolution.determined_decl(); let t = f(self, resolution); - if let Some(binding) = resolution.binding() + if let Some(binding) = resolution.determined_decl() && old_decl != Some(binding) { (binding, t, warn_ambiguity || old_decl.is_some()) @@ -1601,7 +1608,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .iter() .filter_map(|(key, resolution)| { let resolution = resolution.borrow(); - resolution.binding().map(|binding| (*key, binding, resolution.orig_ident_span)) + resolution.determined_decl().map(|decl| (*key, decl, resolution.orig_ident_span)) }) .collect::>(); for (mut key, binding, orig_ident_span) in bindings { @@ -1617,7 +1624,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let import_decl = self.new_import_decl(binding, import); let warn_ambiguity = self .resolution(import.parent_scope.module, key) - .and_then(|r| r.binding()) + .and_then(|r| r.determined_decl()) .is_some_and(|binding| binding.warn_ambiguity_recursive()); let _ = self.try_plant_decl_into_local_module( key.ident, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 72d5cdcf1f3b6..b79b5cc65909e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1101,9 +1101,11 @@ impl<'ra> DeclData<'ra> { !(certainly_before_other_or_simultaneously || certainly_before_invoc_or_simultaneously) } - // Its purpose is to postpone the determination of a single binding because - // we can't predict whether it will be overwritten by recently expanded macros. - // FIXME: How can we integrate it with the `update_resolution`? + /// Returns whether this declaration may be shadowed or overwritten by something else later. + /// FIXME: this function considers `unexpanded_invocations`, but not `single_imports`, so + /// the declaration may not be as "determined" as we think. + /// FIXME: relationship between this function and similar `NameResolution::determined_decl` + /// is unclear. fn determined(&self) -> bool { match &self.kind { DeclKind::Import { source_decl, import, .. } if import.is_glob() => { From 5b84ee80ebc386532c370fa55e1078f69ec98e03 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 26 Mar 2026 17:29:07 +0300 Subject: [PATCH 03/33] resolve: Avoid using `best_decl` in `NameResolution::determined_decl` Using `self.(non_)glob_decl` directly makes it more clear. --- compiler/rustc_resolve/src/imports.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 3103ca32cc0eb..f72a263ee75f4 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -265,13 +265,13 @@ impl<'ra> NameResolution<'ra> { /// code breakage in practice. /// FIXME: relationship between this function and similar `DeclData::determined` is unclear. pub(crate) fn determined_decl(&self) -> Option> { - self.best_decl().and_then(|binding| { - if !binding.is_glob_import() || self.single_imports.is_empty() { - Some(binding) - } else { - None - } - }) + if self.non_glob_decl.is_some() { + self.non_glob_decl + } else if self.glob_decl.is_some() && self.single_imports.is_empty() { + self.glob_decl + } else { + None + } } pub(crate) fn best_decl(&self) -> Option> { From 7559c2c14a9008cf945c9cb6799fa1e4d98f196f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 26 Mar 2026 18:00:28 +0300 Subject: [PATCH 04/33] resolve: Avoid using `best_decl` in `try_plant_decl_into_local_module` Using `self.(non_)glob_decl` directly makes it more clear. --- compiler/rustc_resolve/src/imports.rs | 55 ++++++++++----------------- 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index f72a263ee75f4..34ef4d28ed9ae 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -455,41 +455,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { orig_ident_span, warn_ambiguity, |this, resolution| { - if let Some(old_decl) = resolution.best_decl() { - assert_ne!(decl, old_decl); - assert!(!decl.warn_ambiguity.get()); - if res == Res::Err && old_decl.res() != Res::Err { - // Do not override real declarations with `Res::Err`s from error recovery. - return Ok(()); - } - match (old_decl.is_glob_import(), decl.is_glob_import()) { - (true, true) => { - resolution.glob_decl = - Some(this.select_glob_decl(old_decl, decl, warn_ambiguity)); - } - (old_glob @ true, false) | (old_glob @ false, true) => { - let (glob_decl, non_glob_decl) = - if old_glob { (old_decl, decl) } else { (decl, old_decl) }; - resolution.non_glob_decl = Some(non_glob_decl); - if let Some(old_glob_decl) = resolution.glob_decl - && old_glob_decl != glob_decl - { - resolution.glob_decl = - Some(this.select_glob_decl(old_glob_decl, glob_decl, false)); - } else { - resolution.glob_decl = Some(glob_decl); - } - } - (false, false) => { - return Err(old_decl); - } - } + assert!(!decl.warn_ambiguity.get()); + if res == Res::Err + && let Some(old_decl) = resolution.best_decl() + && old_decl.res() != Res::Err + { + // Do not override real declarations with `Res::Err`s from error recovery. + return Ok(()); + } + if decl.is_glob_import() { + resolution.glob_decl = Some(match resolution.glob_decl { + Some(old_decl) => this.select_glob_decl( + old_decl, + decl, + warn_ambiguity && resolution.non_glob_decl.is_none(), + ), + None => decl, + }) } else { - if decl.is_glob_import() { - resolution.glob_decl = Some(decl); - } else { - resolution.non_glob_decl = Some(decl); - } + resolution.non_glob_decl = Some(match resolution.non_glob_decl { + Some(old_decl) => return Err(old_decl), + None => decl, + }) } Ok(()) From b0b1ebe636907fb6f915b39afafbbd6d05e2226b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 26 Mar 2026 21:06:10 +0300 Subject: [PATCH 05/33] resolve: Remove a special case for dummy imports It is no longer needed to pass import validation, and the removal avoids some secondary errors. Also add a couple of asserts --- compiler/rustc_resolve/src/imports.rs | 18 +++++++----------- tests/ui/imports/issue-56125.rs | 2 +- tests/ui/imports/issue-56125.stderr | 19 +------------------ .../shadow-glob-module-resolution-2.rs | 2 -- .../shadow-glob-module-resolution-2.stderr | 16 +--------------- .../shadow-glob-module-resolution-4.rs | 2 -- .../shadow-glob-module-resolution-4.stderr | 16 +--------------- 7 files changed, 11 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 34ef4d28ed9ae..2b17da085681c 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -456,13 +456,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { warn_ambiguity, |this, resolution| { assert!(!decl.warn_ambiguity.get()); - if res == Res::Err - && let Some(old_decl) = resolution.best_decl() - && old_decl.res() != Res::Err - { - // Do not override real declarations with `Res::Err`s from error recovery. - return Ok(()); - } if decl.is_glob_import() { resolution.glob_decl = Some(match resolution.glob_decl { Some(old_decl) => this.select_glob_decl( @@ -533,13 +526,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; if self.is_accessible_from(binding.vis(), scope) { let import_decl = self.new_import_decl(binding, *import); - let _ = self.try_plant_decl_into_local_module( + self.try_plant_decl_into_local_module( ident, orig_ident_span, key.ns, import_decl, warn_ambiguity, - ); + ) + .expect("planting a glob cannot fail"); } } @@ -558,6 +552,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.per_ns(|this, ns| { let module = import.parent_scope.module; let ident = IdentKey::new(target); + // This can fail, dummies are inserted only in non-occupied slots. let _ = this.try_plant_decl_into_local_module( ident, target.span, @@ -1613,13 +1608,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .resolution(import.parent_scope.module, key) .and_then(|r| r.determined_decl()) .is_some_and(|binding| binding.warn_ambiguity_recursive()); - let _ = self.try_plant_decl_into_local_module( + self.try_plant_decl_into_local_module( key.ident, orig_ident_span, key.ns, import_decl, warn_ambiguity, - ); + ) + .expect("planting a glob cannot fail"); } } diff --git a/tests/ui/imports/issue-56125.rs b/tests/ui/imports/issue-56125.rs index 4e7e7ac67c572..a30ac36473bdd 100644 --- a/tests/ui/imports/issue-56125.rs +++ b/tests/ui/imports/issue-56125.rs @@ -15,7 +15,7 @@ mod m2 { mod m3 { mod empty {} use empty::issue_56125; //~ ERROR unresolved import `empty::issue_56125` - use issue_56125::*; //~ ERROR `issue_56125` is ambiguous + use issue_56125::*; } fn main() {} diff --git a/tests/ui/imports/issue-56125.stderr b/tests/ui/imports/issue-56125.stderr index 371130facf9d3..f9a169b17a2f9 100644 --- a/tests/ui/imports/issue-56125.stderr +++ b/tests/ui/imports/issue-56125.stderr @@ -54,24 +54,7 @@ LL | use issue_56125::non_last_segment::non_last_segment::*; = help: consider adding an explicit import of `issue_56125` to disambiguate = help: or use `self::issue_56125` to refer to this module unambiguously -error[E0659]: `issue_56125` is ambiguous - --> $DIR/issue-56125.rs:18:9 - | -LL | use issue_56125::*; - | ^^^^^^^^^^^ ambiguous name - | - = note: ambiguous because of a conflict between a name from a glob import and an outer scope during import or macro resolution - = note: `issue_56125` could refer to a crate passed with `--extern` - = help: use `::issue_56125` to refer to this crate unambiguously -note: `issue_56125` could also refer to the module imported here - --> $DIR/issue-56125.rs:18:9 - | -LL | use issue_56125::*; - | ^^^^^^^^^^^^^^ - = help: consider adding an explicit import of `issue_56125` to disambiguate - = help: or use `self::issue_56125` to refer to this module unambiguously - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0432, E0659. For more information about an error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/shadow-glob-module-resolution-2.rs b/tests/ui/imports/shadow-glob-module-resolution-2.rs index c3abd1f75542c..ac2901eb35290 100644 --- a/tests/ui/imports/shadow-glob-module-resolution-2.rs +++ b/tests/ui/imports/shadow-glob-module-resolution-2.rs @@ -14,7 +14,5 @@ use a::*; use e as b; //~^ ERROR: unresolved import `e` use b::c::D as e; -//~^ ERROR: cannot determine resolution for the import -//~| ERROR: cannot determine resolution for the import fn main() { } diff --git a/tests/ui/imports/shadow-glob-module-resolution-2.stderr b/tests/ui/imports/shadow-glob-module-resolution-2.stderr index 26745384dee34..ba8a2ce2d29f8 100644 --- a/tests/ui/imports/shadow-glob-module-resolution-2.stderr +++ b/tests/ui/imports/shadow-glob-module-resolution-2.stderr @@ -1,17 +1,3 @@ -error: cannot determine resolution for the import - --> $DIR/shadow-glob-module-resolution-2.rs:16:5 - | -LL | use b::c::D as e; - | ^^^^^^^^^^^^ - -error: cannot determine resolution for the import - --> $DIR/shadow-glob-module-resolution-2.rs:16:5 - | -LL | use b::c::D as e; - | ^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0432]: unresolved import `e` --> $DIR/shadow-glob-module-resolution-2.rs:14:5 | @@ -24,6 +10,6 @@ LL - use e as b; LL + use a as b; | -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/shadow-glob-module-resolution-4.rs b/tests/ui/imports/shadow-glob-module-resolution-4.rs index 581cdc185d3f3..38fe7d17a367f 100644 --- a/tests/ui/imports/shadow-glob-module-resolution-4.rs +++ b/tests/ui/imports/shadow-glob-module-resolution-4.rs @@ -12,8 +12,6 @@ use e as b; use b::C as e; //~^ ERROR: unresolved import `b::C` -//~| ERROR: cannot determine resolution for the import -//~| ERROR: cannot determine resolution for the import fn e() {} diff --git a/tests/ui/imports/shadow-glob-module-resolution-4.stderr b/tests/ui/imports/shadow-glob-module-resolution-4.stderr index 063beb612b132..d94a59347a5b8 100644 --- a/tests/ui/imports/shadow-glob-module-resolution-4.stderr +++ b/tests/ui/imports/shadow-glob-module-resolution-4.stderr @@ -1,23 +1,9 @@ -error: cannot determine resolution for the import - --> $DIR/shadow-glob-module-resolution-4.rs:13:5 - | -LL | use b::C as e; - | ^^^^^^^^^ - -error: cannot determine resolution for the import - --> $DIR/shadow-glob-module-resolution-4.rs:13:5 - | -LL | use b::C as e; - | ^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0432]: unresolved import `b::C` --> $DIR/shadow-glob-module-resolution-4.rs:13:5 | LL | use b::C as e; | ^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0432`. From c775de4beee585977a78a6c647e89b4859f46018 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Tue, 31 Mar 2026 11:29:40 +0700 Subject: [PATCH 06/33] Make `DerefPure` dyn-incompatible Fixes https://github.com/rust-lang/rust/issues/154619. If `DerefPure` were dyn-compatible, a trait object of a subtrait of `DerefPure` could be created by unsize-coercing an existing type that implements `DerefPure`. But then the trait object could have its own non-pure impl of `Deref`/`DerefMut`, which is unsound, since the trait object would implement `DerefPure`. Thus, we make `DerefPure` dyn-incompatible. --- library/core/src/ops/deref.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 305861ea7b698..e46204c6246ab 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -293,6 +293,7 @@ impl const DerefMut for &mut T { /// unchanged. #[unstable(feature = "deref_pure_trait", issue = "87121")] #[lang = "deref_pure"] +#[rustc_dyn_incompatible_trait] pub unsafe trait DerefPure: PointeeSized {} #[unstable(feature = "deref_pure_trait", issue = "87121")] From c7c91173267632dbd4f082d1f27fcb1453be28a9 Mon Sep 17 00:00:00 2001 From: David Gauch Date: Mon, 6 Apr 2026 23:53:29 -0700 Subject: [PATCH 07/33] Add `const Default` impls for `LazyCell` and `LazyLock` --- library/core/src/cell/lazy.rs | 3 ++- library/std/src/sync/lazy_lock.rs | 3 ++- library/std/tests/sync/lazy_lock.rs | 9 +++++++++ library/std/tests/sync/lib.rs | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index ae559e599f481..9d350166c681b 100644 --- a/library/core/src/cell/lazy.rs +++ b/library/core/src/cell/lazy.rs @@ -346,7 +346,8 @@ impl T> DerefMut for LazyCell { } #[stable(feature = "lazy_cell", since = "1.80.0")] -impl Default for LazyCell { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for LazyCell { /// Creates a new lazy value using `Default` as the initializing function. #[inline] fn default() -> LazyCell { diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 9bdde0ae4a537..de1b9c391e8f2 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -375,7 +375,8 @@ impl T> DerefMut for LazyLock { } #[stable(feature = "lazy_cell", since = "1.80.0")] -impl Default for LazyLock { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for LazyLock { /// Creates a new lazy value using `Default` as the initializing function. #[inline] fn default() -> LazyLock { diff --git a/library/std/tests/sync/lazy_lock.rs b/library/std/tests/sync/lazy_lock.rs index 68aeea834b4fa..c549094dd1381 100644 --- a/library/std/tests/sync/lazy_lock.rs +++ b/library/std/tests/sync/lazy_lock.rs @@ -33,6 +33,15 @@ fn lazy_default() { assert_eq!(CALLED.load(SeqCst), 1); } +#[test] +fn const_lazy_default() { + // using Box as it cannot be initialized in const contexts + const X: LazyLock> = <_>::default(); + const Y: LazyCell> = <_>::default(); + assert_eq!(**X, 0); + assert_eq!(**Y, 0); +} + #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn sync_lazy_new() { diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index 32a7efde2a257..92c4c2f511be3 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -1,3 +1,5 @@ +#![feature(const_default)] +#![feature(const_trait_impl)] #![feature(mapped_lock_guards)] #![feature(mpmc_channel)] #![feature(oneshot_channel)] From aa6150b6803ef999b95ea40b3b6776f96acef569 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Tue, 7 Apr 2026 13:51:25 +0300 Subject: [PATCH 08/33] Refactor arena_cache query values --- compiler/rustc_middle/src/queries.rs | 4 ++-- compiler/rustc_middle/src/query/arena_cached.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 1017ccffb0b2a..d3b1345e93701 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1202,7 +1202,7 @@ rustc_queries! { /// Return the live symbols in the crate for dead code check. /// /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone). - query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx Result<( + query live_symbols_and_ignored_derived_traits(_: ()) -> Result<&'tcx ( LocalDefIdSet, LocalDefIdMap>, ), ErrorGuaranteed> { @@ -1292,7 +1292,7 @@ rustc_queries! { /// Return the set of (transitive) callees that may result in a recursive call to `key`, /// if we were able to walk all callees. - query mir_callgraph_cyclic(key: LocalDefId) -> &'tcx Option> { + query mir_callgraph_cyclic(key: LocalDefId) -> Option<&'tcx UnordSet> { arena_cache desc { "computing (transitive) callees of `{}` that may recurse", diff --git a/compiler/rustc_middle/src/query/arena_cached.rs b/compiler/rustc_middle/src/query/arena_cached.rs index 7c7ad12622604..4ab2fbe914965 100644 --- a/compiler/rustc_middle/src/query/arena_cached.rs +++ b/compiler/rustc_middle/src/query/arena_cached.rs @@ -1,6 +1,7 @@ use std::mem; use rustc_arena::TypedArena; +use rustc_span::ErrorGuaranteed; use crate::ty::TyCtxt; @@ -51,6 +52,21 @@ impl<'tcx, T> ArenaCached<'tcx> for Option<&'tcx T> { } } +impl<'tcx, T> ArenaCached<'tcx> for Result<&'tcx T, ErrorGuaranteed> { + type Provided = Result; + /// The provide value is `Result`, but we only store `T` in the arena. + type Allocated = T; + + fn alloc_in_arena( + tcx: TyCtxt<'tcx>, + typed_arena: &'tcx TypedArena, + value: Result, + ) -> Self { + // Don't store Err(ErrorGuaranteed) in the arena, and wrap the allocated reference in Ok. + try { do_alloc(tcx, typed_arena, value?) } + } +} + /// Allocates a value in either its dedicated arena, or in the common dropless /// arena, depending on whether it needs to be dropped. fn do_alloc<'tcx, T>(tcx: TyCtxt<'tcx>, typed_arena: &'tcx TypedArena, value: T) -> &'tcx T { From fe309a0beda6990171ec3fcac36128eb039c48fb Mon Sep 17 00:00:00 2001 From: Shun Sakai Date: Sat, 11 Apr 2026 07:46:34 +0900 Subject: [PATCH 09/33] Stabilize feature `uint_bit_width` --- library/core/src/num/nonzero.rs | 5 ++--- library/core/src/num/uint_macros.rs | 5 ++--- library/coretests/tests/lib.rs | 1 - 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index c270b947d4fd3..9d696e130fdc8 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1950,8 +1950,6 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Examples /// /// ``` - /// #![feature(uint_bit_width)] - /// /// # use core::num::NonZero; /// # /// # fn main() { test().unwrap(); } @@ -1962,7 +1960,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Some(()) /// # } /// ``` - #[unstable(feature = "uint_bit_width", issue = "142326")] + #[stable(feature = "uint_bit_width", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "uint_bit_width", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index c35deee920e46..8a0b5a9c77ecf 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -232,14 +232,13 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(uint_bit_width)] - /// #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".bit_width(), 0);")] #[doc = concat!("assert_eq!(0b111_", stringify!($SelfT), ".bit_width(), 3);")] #[doc = concat!("assert_eq!(0b1110_", stringify!($SelfT), ".bit_width(), 4);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.bit_width(), ", stringify!($BITS), ");")] /// ``` - #[unstable(feature = "uint_bit_width", issue = "142326")] + #[stable(feature = "uint_bit_width", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "uint_bit_width", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 129d2c013cd27..d435e01a98c62 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -121,7 +121,6 @@ #![feature(try_find)] #![feature(try_trait_v2)] #![feature(type_info)] -#![feature(uint_bit_width)] #![feature(uint_carryless_mul)] #![feature(uint_gather_scatter_bits)] #![feature(unicode_internals)] From e756d1607d6af1e8a76e5b1e7bf8821db990772e Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 26 Mar 2026 16:45:33 +0100 Subject: [PATCH 10/33] Add `eii_impls` argument to `StaticItem` --- compiler/rustc_ast/src/ast.rs | 7 ++++ compiler/rustc_ast_lowering/src/item.rs | 13 +++++-- .../rustc_ast_pretty/src/pprust/state/item.rs | 38 +++++++++++++------ compiler/rustc_expand/src/build.rs | 1 + compiler/rustc_parse/src/parser/item.rs | 12 +++++- compiler/rustc_resolve/src/def_collector.rs | 1 + .../clippy/clippy_utils/src/ast_utils/mod.rs | 4 ++ 7 files changed, 60 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ae4989fcbc6c9..ad32fe7e488c1 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3918,6 +3918,13 @@ pub struct StaticItem { pub mutability: Mutability, pub expr: Option>, pub define_opaque: Option>, + + /// This static is an implementation of an externally implementable item (EII). + /// This means, there was an EII declared somewhere and this static is the + /// implementation that should be used for the declaration. + /// + /// For statics, there may be at most one `EiiImpl`, but this is a `ThinVec` to make usages of this field nicer. + pub eii_impls: ThinVec, } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 072d4803bf459..d9c2c12113199 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -213,8 +213,14 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { i: &ItemKind, ) -> Vec { match i { - ItemKind::Fn(box Fn { eii_impls, .. }) if eii_impls.is_empty() => Vec::new(), - ItemKind::Fn(box Fn { eii_impls, .. }) => { + ItemKind::Fn(box Fn { eii_impls, .. }) + | ItemKind::Static(box StaticItem { eii_impls, .. }) + if eii_impls.is_empty() => + { + Vec::new() + } + ItemKind::Fn(box Fn { eii_impls, .. }) + | ItemKind::Static(box StaticItem { eii_impls, .. }) => { vec![hir::Attribute::Parsed(AttributeKind::EiiImpls( eii_impls.iter().map(|i| self.lower_eii_impl(i)).collect(), ))] @@ -226,7 +232,6 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { ItemKind::ExternCrate(..) | ItemKind::Use(..) - | ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::ConstBlock(..) | ItemKind::Mod(..) @@ -302,6 +307,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { mutability: m, expr: e, define_opaque, + eii_impls: _, }) => { let ident = self.lower_ident(*ident); let ty = self @@ -817,6 +823,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { expr: _, safety, define_opaque, + eii_impls: _, }) => { let ty = self .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 3e9c596148343..201fa63bfa33c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -43,6 +43,7 @@ impl<'a> State<'a> { expr, safety, define_opaque, + eii_impls, }) => self.print_item_const( *ident, Some(*mutability), @@ -53,6 +54,7 @@ impl<'a> State<'a> { *safety, ast::Defaultness::Implicit, define_opaque.as_deref(), + eii_impls, ), ast::ForeignItemKind::TyAlias(box ast::TyAlias { defaultness, @@ -93,8 +95,12 @@ impl<'a> State<'a> { safety: ast::Safety, defaultness: ast::Defaultness, define_opaque: Option<&[(ast::NodeId, ast::Path)]>, + eii_impls: &[EiiImpl], ) { self.print_define_opaques(define_opaque); + for eii_impl in eii_impls { + self.print_eii_impl(eii_impl); + } let (cb, ib) = self.head(""); self.print_visibility(vis); self.print_safety(safety); @@ -191,6 +197,7 @@ impl<'a> State<'a> { mutability: mutbl, expr: body, define_opaque, + eii_impls, }) => { self.print_safety(*safety); self.print_item_const( @@ -203,6 +210,7 @@ impl<'a> State<'a> { ast::Safety::Default, ast::Defaultness::Implicit, define_opaque.as_deref(), + eii_impls, ); } ast::ItemKind::ConstBlock(ast::ConstBlockItem { id: _, span: _, block }) => { @@ -234,6 +242,7 @@ impl<'a> State<'a> { ast::Safety::Default, *defaultness, define_opaque.as_deref(), + &[], ); } ast::ItemKind::Fn(func) => { @@ -602,6 +611,7 @@ impl<'a> State<'a> { ast::Safety::Default, *defaultness, define_opaque.as_deref(), + &[], ); } ast::AssocItemKind::Type(box ast::TyAlias { @@ -703,18 +713,8 @@ impl<'a> State<'a> { self.print_define_opaques(define_opaque.as_deref()); - for EiiImpl { eii_macro_path, impl_safety, .. } in eii_impls { - self.word("#["); - if let Safety::Unsafe(..) = impl_safety { - self.word("unsafe"); - self.popen(); - } - self.print_path(eii_macro_path, false, 0); - if let Safety::Unsafe(..) = impl_safety { - self.pclose(); - } - self.word("]"); - self.hardbreak(); + for eii_impl in eii_impls { + self.print_eii_impl(eii_impl); } let body_cb_ib = body.as_ref().map(|body| (body, self.head(""))); @@ -741,6 +741,20 @@ impl<'a> State<'a> { } } + fn print_eii_impl(&mut self, eii: &ast::EiiImpl) { + self.word("#["); + if let Safety::Unsafe(..) = eii.impl_safety { + self.word("unsafe"); + self.popen(); + } + self.print_path(&eii.eii_macro_path, false, 0); + if let Safety::Unsafe(..) = eii.impl_safety { + self.pclose(); + } + self.word("]"); + self.hardbreak(); + } + fn print_define_opaques(&mut self, define_opaque: Option<&[(ast::NodeId, ast::Path)]>) { if let Some(define_opaque) = define_opaque { self.word("#[define_opaque("); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 05e6b78132aeb..01886a97f55a2 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -715,6 +715,7 @@ impl<'a> ExtCtxt<'a> { mutability, expr: Some(expr), define_opaque: None, + eii_impls: Default::default(), } .into(), ), diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index ab3683f598208..df85aa7d041a5 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1235,6 +1235,7 @@ impl<'a> Parser<'a> { mutability: _, expr, define_opaque, + eii_impls: _, }) => { self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Box::new(ConstItem { @@ -1503,6 +1504,7 @@ impl<'a> Parser<'a> { }, safety: Safety::Default, define_opaque: None, + eii_impls: ThinVec::default(), })) } _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), @@ -1636,7 +1638,15 @@ impl<'a> Parser<'a> { self.expect_semi()?; - let item = StaticItem { ident, ty, safety, mutability, expr, define_opaque: None }; + let item = StaticItem { + ident, + ty, + safety, + mutability, + expr, + define_opaque: None, + eii_impls: ThinVec::default(), + }; Ok(ItemKind::Static(Box::new(item))) } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 427a75c6bff4d..78dcb0620e5b1 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -274,6 +274,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { expr: _, safety, define_opaque: _, + eii_impls: _, }) => { let safety = match safety { ast::Safety::Unsafe(_) | ast::Safety::Default => hir::Safety::Unsafe, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index cfff7da60a6a4..c96c0649753fd 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -339,6 +339,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { expr: le, safety: ls, define_opaque: _, + eii_impls: _, }), Static(box StaticItem { ident: ri, @@ -347,6 +348,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { expr: re, safety: rs, define_opaque: _, + eii_impls: _, }), ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref()), ( @@ -540,6 +542,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { expr: le, safety: ls, define_opaque: _, + eii_impls: _, }), Static(box StaticItem { ident: ri, @@ -548,6 +551,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { expr: re, safety: rs, define_opaque: _, + eii_impls: _, }), ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_deref(), re.as_deref()) && ls == rs, ( From 607b0620e2bfd8ee0c7ed42e83ec35aa88ed6984 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 27 Mar 2026 22:21:22 +0100 Subject: [PATCH 11/33] Analysis for externally implementable statics --- .../src/attributes/codegen_attrs.rs | 3 +- compiler/rustc_builtin_macros/src/eii.rs | 158 +++++++++++------- compiler/rustc_builtin_macros/src/errors.rs | 20 ++- .../src/check/compare_eii.rs | 132 +++++++++++++-- .../rustc_hir_analysis/src/check/wfcheck.rs | 37 +++- compiler/rustc_hir_analysis/src/errors.rs | 25 +++ compiler/rustc_passes/src/check_attr.rs | 4 +- compiler/rustc_passes/src/errors.rs | 4 +- compiler/rustc_resolve/src/late.rs | 43 +++-- 9 files changed, 328 insertions(+), 98 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index ff94cf50adf67..9625a1f04f26e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -727,7 +727,8 @@ pub(crate) struct RustcEiiForeignItemParser; impl NoArgsAttributeParser for RustcEiiForeignItemParser { const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem; } diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 7b651ed848288..9dab90b72a027 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -10,8 +10,8 @@ use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, - EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroExpectedFunction, - EiiSharedMacroInStatementPosition, + EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, + EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, }; /// ```rust @@ -73,44 +73,63 @@ fn eii_( }); return vec![orig_item]; } else { - ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { + ecx.dcx().emit_err(EiiSharedMacroTarget { span: eii_attr_span, name: path_to_string(&meta_item.path), }); return vec![orig_item]; }; - let ast::Item { attrs, id: _, span: _, vis, kind: ItemKind::Fn(func), tokens: _ } = - item.as_ref() - else { - ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { - span: eii_attr_span, - name: path_to_string(&meta_item.path), - }); - return vec![Annotatable::Item(item)]; + let ast::Item { attrs, id: _, span: _, vis, kind, tokens: _ } = item.as_ref(); + let (item_span, foreign_item_name) = match kind { + ItemKind::Fn(func) => (func.sig.span, func.ident), + ItemKind::Static(stat) => { + // Statics with a default are not supported yet + if let Some(stat_body) = &stat.expr { + ecx.dcx().emit_err(EiiStaticDefault { + span: stat_body.span, + name: path_to_string(&meta_item.path), + }); + return vec![]; + } + // Statics must have an explicit name for the eii + if meta_item.is_word() { + ecx.dcx().emit_err(EiiStaticArgumentRequired { + span: eii_attr_span, + name: path_to_string(&meta_item.path), + }); + return vec![]; + } + (item.span, stat.ident) + } + _ => { + ecx.dcx().emit_err(EiiSharedMacroTarget { + span: eii_attr_span, + name: path_to_string(&meta_item.path), + }); + return vec![Annotatable::Item(item)]; + } }; + // only clone what we need let attrs = attrs.clone(); - let func = (**func).clone(); let vis = vis.clone(); let attrs_from_decl = filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path); - let Ok(macro_name) = name_for_impl_macro(ecx, &func, &meta_item) else { + let Ok(macro_name) = name_for_impl_macro(ecx, foreign_item_name, &meta_item) else { // we don't need to wrap in Annotatable::Stmt conditionally since // EII can't be used on items in statement position return vec![Annotatable::Item(item)]; }; - // span of the declaring item without attributes - let item_span = func.sig.span; - let foreign_item_name = func.ident; - let mut module_items = Vec::new(); - if func.body.is_some() { - module_items.push(generate_default_impl( + if let ItemKind::Fn(func) = kind + && func.body.is_some() + { + module_items.push(generate_default_func_impl( ecx, &func, impl_unsafe, @@ -125,7 +144,7 @@ fn eii_( ecx, eii_attr_span, item_span, - func, + kind, vis, &attrs_from_decl, )); @@ -148,11 +167,11 @@ fn eii_( /// declaration of the EII. fn name_for_impl_macro( ecx: &mut ExtCtxt<'_>, - func: &ast::Fn, + item_ident: Ident, meta_item: &MetaItem, ) -> Result { if meta_item.is_word() { - Ok(func.ident) + Ok(item_ident) } else if let Some([first]) = meta_item.meta_item_list() && let Some(m) = first.meta_item() && m.path.segments.len() == 1 @@ -190,7 +209,7 @@ fn filter_attrs_for_multiple_eii_attr( .collect() } -fn generate_default_impl( +fn generate_default_func_impl( ecx: &mut ExtCtxt<'_>, func: &ast::Fn, impl_unsafe: bool, @@ -257,7 +276,7 @@ fn generate_foreign_item( ecx: &mut ExtCtxt<'_>, eii_attr_span: Span, item_span: Span, - mut func: ast::Fn, + item_kind: &ItemKind, vis: Visibility, attrs_from_decl: &[Attribute], ) -> Box { @@ -268,30 +287,21 @@ fn generate_foreign_item( // This attribute makes sure that we later know that this foreign item's symbol should not be. foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_foreign_item, eii_attr_span)); - let abi = match func.sig.header.ext { - // extern "X" fn => extern "X" {} - ast::Extern::Explicit(lit, _) => Some(lit), - // extern fn => extern {} - ast::Extern::Implicit(_) => None, - // fn => extern "Rust" {} - ast::Extern::None => Some(ast::StrLit { - symbol: sym::Rust, - suffix: None, - symbol_unescaped: sym::Rust, - style: ast::StrStyle::Cooked, - span: eii_attr_span, - }), + // We set the abi to the default "rust" abi, which can be overridden by `generate_foreign_func`, + // if a specific abi was specified on the EII function + let mut abi = Some(ast::StrLit { + symbol: sym::Rust, + suffix: None, + symbol_unescaped: sym::Rust, + style: ast::StrStyle::Cooked, + span: eii_attr_span, + }); + let foreign_kind = match item_kind { + ItemKind::Fn(func) => generate_foreign_func(func.clone(), &mut abi), + ItemKind::Static(stat) => generate_foreign_static(stat.clone()), + _ => unreachable!("Target was checked earlier"), }; - // ABI has been moved to the extern {} block, so we remove it from the fn item. - func.sig.header.ext = ast::Extern::None; - func.body = None; - - // And mark safe functions explicitly as `safe fn`. - if func.sig.header.safety == ast::Safety::Default { - func.sig.header.safety = ast::Safety::Safe(func.sig.span); - } - ecx.item( eii_attr_span, ThinVec::new(), @@ -304,13 +314,46 @@ fn generate_foreign_item( id: ast::DUMMY_NODE_ID, span: item_span, vis, - kind: ast::ForeignItemKind::Fn(Box::new(func.clone())), + kind: foreign_kind, tokens: None, })]), }), ) } +fn generate_foreign_func( + mut func: Box, + abi: &mut Option, +) -> ast::ForeignItemKind { + match func.sig.header.ext { + // extern "X" fn => extern "X" {} + ast::Extern::Explicit(lit, _) => *abi = Some(lit), + // extern fn => extern {} + ast::Extern::Implicit(_) => *abi = None, + // no abi was specified, so we keep the default + ast::Extern::None => {} + }; + + // ABI has been moved to the extern {} block, so we remove it from the fn item. + func.sig.header.ext = ast::Extern::None; + func.body = None; + + // And mark safe functions explicitly as `safe fn`. + if func.sig.header.safety == ast::Safety::Default { + func.sig.header.safety = ast::Safety::Safe(func.sig.span); + } + + ast::ForeignItemKind::Fn(func) +} + +fn generate_foreign_static(mut stat: Box) -> ast::ForeignItemKind { + if stat.safety == ast::Safety::Default { + stat.safety = ast::Safety::Safe(stat.ident.span); + } + + ast::ForeignItemKind::Static(stat) +} + /// Generate a stub macro (a bit like in core) that will roughly look like: /// /// ```rust, ignore, example @@ -453,19 +496,18 @@ pub(crate) fn eii_shared_macro( { item } else { - ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { - span, - name: path_to_string(&meta_item.path), - }); + ecx.dcx().emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) }); return vec![item]; }; - let ItemKind::Fn(f) = &mut i.kind else { - ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { - span, - name: path_to_string(&meta_item.path), - }); - return vec![item]; + let eii_impls = match &mut i.kind { + ItemKind::Fn(func) => &mut func.eii_impls, + ItemKind::Static(stat) => &mut stat.eii_impls, + _ => { + ecx.dcx() + .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) }); + return vec![item]; + } }; let is_default = if meta_item.is_word() { @@ -483,7 +525,7 @@ pub(crate) fn eii_shared_macro( return vec![item]; }; - f.eii_impls.push(EiiImpl { + eii_impls.push(EiiImpl { node_id: DUMMY_NODE_ID, inner_span: meta_item.path.span, eii_macro_path: meta_item.path.clone(), diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index b5ac84337465a..4b572c2f34ac0 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1117,8 +1117,24 @@ pub(crate) struct EiiExternTargetExpectedUnsafe { } #[derive(Diagnostic)] -#[diag("`#[{$name}]` is only valid on functions")] -pub(crate) struct EiiSharedMacroExpectedFunction { +#[diag("`#[{$name}]` is only valid on functions and statics")] +pub(crate) struct EiiSharedMacroTarget { + #[primary_span] + pub span: Span, + pub name: String, +} + +#[derive(Diagnostic)] +#[diag("`#[{$name}]` cannot be used on statics with a value")] +pub(crate) struct EiiStaticDefault { + #[primary_span] + pub span: Span, + pub name: String, +} + +#[derive(Diagnostic)] +#[diag("`#[{$name}]` requires the name as an explicit argument when used on a static")] +pub(crate) struct EiiStaticArgumentRequired { #[primary_span] pub span: Span, pub name: String, diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index 29213058d1d5e..956e68773b796 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -15,7 +15,7 @@ use rustc_hir::{self as hir, FnSig, HirId, ItemKind, find_attr}; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::regions::InferCtxtRegionExt; @@ -26,7 +26,10 @@ use super::potentially_plural_count; use crate::check::compare_impl_item::{ CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions, }; -use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii}; +use crate::errors::{ + EiiDefkindMismatch, EiiDefkindMismatchStaticMutability, EiiDefkindMismatchStaticSafety, + EiiWithGenerics, LifetimesOrBoundsMismatchOnEii, +}; /// Checks whether the signature of some `external_impl`, matches /// the signature of `declaration`, which it is supposed to be compatible @@ -38,14 +41,7 @@ pub(crate) fn compare_eii_function_types<'tcx>( eii_name: Symbol, eii_attr_span: Span, ) -> Result<(), ErrorGuaranteed> { - // Error recovery can resolve the EII target to another value item with the same name, - // such as a tuple-struct constructor. Skip the comparison in that case and rely on the - // earlier name-resolution error instead of ICEing while building EII diagnostics. - // See . - if !is_foreign_function(tcx, foreign_item) { - return Ok(()); - } - + check_eii_target(tcx, external_impl, foreign_item, eii_name, eii_attr_span)?; check_is_structurally_compatible(tcx, external_impl, foreign_item, eii_name, eii_attr_span)?; let external_impl_span = tcx.def_span(external_impl); @@ -152,6 +148,118 @@ pub(crate) fn compare_eii_function_types<'tcx>( Ok(()) } +pub(crate) fn compare_eii_statics<'tcx>( + tcx: TyCtxt<'tcx>, + external_impl: LocalDefId, + external_impl_ty: Ty<'tcx>, + foreign_item: DefId, + eii_name: Symbol, + eii_attr_span: Span, +) -> Result<(), ErrorGuaranteed> { + check_eii_target(tcx, external_impl, foreign_item, eii_name, eii_attr_span)?; + + let external_impl_span = tcx.def_span(external_impl); + let cause = ObligationCause::new( + external_impl_span, + external_impl, + ObligationCauseCode::CompareEii { external_impl, declaration: foreign_item }, + ); + + let param_env = ParamEnv::empty(); + + let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); + + let declaration_ty = tcx.type_of(foreign_item).instantiate_identity(); + debug!(?declaration_ty); + + // FIXME: Copied over from compare impl items, same issue: + // We'd want to keep more accurate spans than "the method signature" when + // processing the comparison between the trait and impl fn, but we sadly lose them + // and point at the whole signature when a trait bound or specific input or output + // type would be more appropriate. In other places we have a `Vec` + // corresponding to their `Vec`, but we don't have that here. + // Fixing this would improve the output of test `issue-83765.rs`. + let result = ocx.sup(&cause, param_env, declaration_ty, external_impl_ty); + + if let Err(terr) = result { + debug!(?external_impl_ty, ?declaration_ty, ?terr, "sub_types failed"); + + let mut diag = struct_span_code_err!( + tcx.dcx(), + cause.span, + E0806, + "static `{}` has a type that is incompatible with the declaration of `#[{eii_name}]`", + tcx.item_name(external_impl) + ); + diag.span_note(eii_attr_span, "expected this because of this attribute"); + + return Err(diag.emit()); + } + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + if !errors.is_empty() { + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); + return Err(reported); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let errors = infcx.resolve_regions(external_impl, param_env, []); + if !errors.is_empty() { + return Err(infcx + .tainted_by_errors() + .unwrap_or_else(|| infcx.err_ctxt().report_region_errors(external_impl, &errors))); + } + + Ok(()) +} + +fn check_eii_target( + tcx: TyCtxt<'_>, + external_impl: LocalDefId, + foreign_item: DefId, + eii_name: Symbol, + eii_attr_span: Span, +) -> Result<(), ErrorGuaranteed> { + // Error recovery can resolve the EII target to another value item with the same name, + // such as a tuple-struct constructor. Skip the comparison in that case and rely on the + // earlier name-resolution error instead of ICEing while building EII diagnostics. + // See . + if !tcx.is_foreign_item(foreign_item) { + return Err(tcx.dcx().delayed_bug("EII is a foreign item")); + } + let expected_kind = tcx.def_kind(foreign_item); + let actual_kind = tcx.def_kind(external_impl); + + match expected_kind { + // Correct target + _ if expected_kind == actual_kind => Ok(()), + DefKind::Static { mutability: m1, safety: s1, .. } + if let DefKind::Static { mutability: m2, safety: s2, .. } = actual_kind => + { + Err(if s1 != s2 { + tcx.dcx().emit_err(EiiDefkindMismatchStaticSafety { span: eii_attr_span, eii_name }) + } else if m1 != m2 { + tcx.dcx() + .emit_err(EiiDefkindMismatchStaticMutability { span: eii_attr_span, eii_name }) + } else { + unreachable!() + }) + } + // Not checked by attr target checking + DefKind::Fn | DefKind::Static { .. } => Err(tcx.dcx().emit_err(EiiDefkindMismatch { + span: eii_attr_span, + eii_name, + expected_kind: expected_kind.descr(foreign_item), + })), + // Checked by attr target checking + _ => Err(tcx.dcx().delayed_bug("Attribute should not be allowed by target checking")), + } +} + /// Checks a bunch of different properties of the impl/trait methods for /// compatibility, such as asyncness, number of argument, self receiver kind, /// and number of early- and late-bound generics. @@ -451,7 +559,3 @@ fn get_declaration_sig<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Option<&' let hir_id: HirId = tcx.local_def_id_to_hir_id(def_id); tcx.hir_fn_sig_by_hir_id(hir_id) } - -fn is_foreign_function(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.is_foreign_item(def_id) && matches!(tcx.def_kind(def_id), DefKind::Fn) -} diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c7b87db5971fa..88f8abaf19e75 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -41,7 +41,7 @@ use rustc_trait_selection::traits::{ }; use tracing::{debug, instrument}; -use super::compare_eii::compare_eii_function_types; +use super::compare_eii::{compare_eii_function_types, compare_eii_statics}; use crate::autoderef::Autoderef; use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params}; use crate::errors; @@ -1208,7 +1208,7 @@ fn check_item_fn( decl: &hir::FnDecl<'_>, ) -> Result<(), ErrorGuaranteed> { enter_wf_checking_ctxt(tcx, def_id, |wfcx| { - check_eiis(tcx, def_id); + check_eiis_fn(tcx, def_id); let sig = tcx.fn_sig(def_id).instantiate_identity(); check_fn_or_method(wfcx, sig, decl, def_id); @@ -1216,7 +1216,7 @@ fn check_item_fn( }) } -fn check_eiis(tcx: TyCtxt<'_>, def_id: LocalDefId) { +fn check_eiis_fn(tcx: TyCtxt<'_>, def_id: LocalDefId) { // does the function have an EiiImpl attribute? that contains the defid of a *macro* // that was used to mark the implementation. This is a two step process. for EiiImpl { resolution, span, .. } in @@ -1243,6 +1243,33 @@ fn check_eiis(tcx: TyCtxt<'_>, def_id: LocalDefId) { } } +fn check_eiis_static<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, ty: Ty<'tcx>) { + // does the function have an EiiImpl attribute? that contains the defid of a *macro* + // that was used to mark the implementation. This is a two step process. + for EiiImpl { resolution, span, .. } in + find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_iter().flatten() + { + let (foreign_item, name) = match resolution { + EiiImplResolution::Macro(def_id) => { + // we expect this macro to have the `EiiMacroFor` attribute, that points to a function + // signature that we'd like to compare the function we're currently checking with + if let Some(foreign_item) = + find_attr!(tcx, *def_id, EiiDeclaration(EiiDecl {foreign_item: t, ..}) => *t) + { + (foreign_item, tcx.item_name(*def_id)) + } else { + tcx.dcx().span_delayed_bug(*span, "resolved to something that's not an EII"); + continue; + } + } + EiiImplResolution::Known(decl) => (decl.foreign_item, decl.name.name), + EiiImplResolution::Error(_eg) => continue, + }; + + let _ = compare_eii_statics(tcx, def_id, ty, foreign_item, name, *span); + } +} + #[instrument(level = "debug", skip(tcx))] pub(crate) fn check_static_item<'tcx>( tcx: TyCtxt<'tcx>, @@ -1251,6 +1278,10 @@ pub(crate) fn check_static_item<'tcx>( should_check_for_sync: bool, ) -> Result<(), ErrorGuaranteed> { enter_wf_checking_ctxt(tcx, item_id, |wfcx| { + if should_check_for_sync { + check_eiis_static(tcx, item_id, ty); + } + let span = tcx.ty_span(item_id); let loc = Some(WellFormedLoc::Ty(item_id)); let item_ty = wfcx.deeply_normalize(span, loc, ty); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c55b9e384c55b..4e56909260ad1 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1923,3 +1923,28 @@ pub(crate) struct ImplUnpinForPinProjectedType { pub adt_span: Span, pub adt_name: Symbol, } + +#[derive(Diagnostic)] +#[diag("`#[{$eii_name}]` must be used on a {$expected_kind}")] +pub(crate) struct EiiDefkindMismatch { + #[primary_span] + pub span: Span, + pub eii_name: Symbol, + pub expected_kind: &'static str, +} + +#[derive(Diagnostic)] +#[diag("mutability does not match with the definition of`#[{$eii_name}]`")] +pub(crate) struct EiiDefkindMismatchStaticMutability { + #[primary_span] + pub span: Span, + pub eii_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("safety does not match with the definition of`#[{$eii_name}]`")] +pub(crate) struct EiiDefkindMismatchStaticSafety { + #[primary_span] + pub span: Span, + pub eii_name: Symbol, +} diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 896e9d01777ff..4f81f91300307 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -551,9 +551,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) { for EiiImpl { span, inner_span, resolution, impl_marked_unsafe, is_default: _ } in impls { match target { - Target::Fn => {} + Target::Fn | Target::Static => {} _ => { - self.dcx().emit_err(errors::EiiImplNotFunction { span: *span }); + self.dcx().emit_err(errors::EiiImplTarget { span: *span }); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 5de43f24b2dc6..9d4f7a3cbbea2 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1175,8 +1175,8 @@ pub(crate) struct ReprAlignShouldBeAlignStatic { } #[derive(Diagnostic)] -#[diag("`eii_macro_for` is only valid on functions")] -pub(crate) struct EiiImplNotFunction { +#[diag("`eii_macro_for` is only valid on functions and statics")] +pub(crate) struct EiiImplTarget { #[primary_span] pub span: Span, } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2c287045be7a9..540e7ec52f51c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1095,21 +1095,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc debug!("(resolving function) entering function"); if let FnKind::Fn(_, _, f) = fn_kind { - for EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. } in &f.eii_impls - { - // See docs on the `known_eii_macro_resolution` field: - // if we already know the resolution statically, don't bother resolving it. - if let Some(target) = known_eii_macro_resolution { - self.smart_resolve_path( - *node_id, - &None, - &target.foreign_item, - PathSource::ExternItemImpl, - ); - } else { - self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); - } - } + self.resolve_eii(&f.eii_impls); } // Create a value rib for the function. @@ -2905,7 +2891,14 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.parent_scope.module = orig_module; } - ItemKind::Static(box ast::StaticItem { ident, ty, expr, define_opaque, .. }) => { + ItemKind::Static(box ast::StaticItem { + ident, + ty, + expr, + define_opaque, + eii_impls, + .. + }) => { self.with_static_rib(def_kind, |this| { this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { this.visit_ty(ty); @@ -2917,6 +2910,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } }); self.resolve_define_opaques(define_opaque); + self.resolve_eii(&eii_impls); } ItemKind::Const(box ast::ConstItem { @@ -5496,6 +5490,23 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } } + + fn resolve_eii(&mut self, eii_impls: &[EiiImpl]) { + for EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. } in eii_impls { + // See docs on the `known_eii_macro_resolution` field: + // if we already know the resolution statically, don't bother resolving it. + if let Some(target) = known_eii_macro_resolution { + self.smart_resolve_path( + *node_id, + &None, + &target.foreign_item, + PathSource::ExternItemImpl + ); + } else { + self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); + } + } + } } /// Walks the whole crate in DFS order, visiting each item, counting the declared number of From c7e194f215fc5e4273a12438eded7e79c95d9c57 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 26 Mar 2026 16:47:34 +0100 Subject: [PATCH 12/33] Fix codegen for `add_static_aliases` --- compiler/rustc_codegen_llvm/src/mono_item.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 0783282bc6e56..2c0a6ff01018c 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -135,7 +135,8 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { let ty = self.get_type_of_global(aliasee); for (alias, linkage, visibility) in aliases { - let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias)); + let instance = Instance::mono(self.tcx, *alias); + let symbol_name = self.tcx.symbol_name(instance); tracing::debug!("STATIC ALIAS: {alias:?} {linkage:?} {visibility:?}"); let lldecl = llvm::add_alias( @@ -145,6 +146,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { aliasee, &CString::new(symbol_name.name).unwrap(), ); + // Add the alias name to the set of cached items, so there is no duplicate + // instance added to it during the normal `external static` codegen + let prev_entry = self.instances.borrow_mut().insert(instance, lldecl); + + // If there already was a previous entry, then `add_static_aliases` was called multiple times for the same `alias` + // which would result in incorrect codegen + assert!(prev_entry.is_none(), "An instance was already present for {instance:?}"); llvm::set_visibility(lldecl, base::visibility_to_llvm(*visibility)); llvm::set_linkage(lldecl, base::linkage_to_llvm(*linkage)); From fca29ada741148b90f3afafa2926c1447733376b Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 26 Mar 2026 16:47:53 +0100 Subject: [PATCH 13/33] Uitests for external statics --- tests/ui/eii/attribute_targets.stderr | 36 +++++++++---------- tests/ui/eii/default/call_default.rs | 2 +- tests/ui/eii/default/call_default_panics.rs | 2 +- tests/ui/eii/default/call_impl.rs | 2 +- tests/ui/eii/default/local_crate.rs | 2 +- tests/ui/eii/default/local_crate_explicit.rs | 2 +- tests/ui/eii/duplicate/duplicate1.rs | 2 +- tests/ui/eii/duplicate/duplicate2.rs | 2 +- tests/ui/eii/duplicate/duplicate3.rs | 2 +- tests/ui/eii/duplicate/multiple_impls.rs | 2 +- tests/ui/eii/error_statement_position.stderr | 2 +- tests/ui/eii/errors.rs | 6 ++-- tests/ui/eii/errors.stderr | 20 ++++------- tests/ui/eii/linking/codegen_cross_crate.rs | 2 +- tests/ui/eii/linking/codegen_single_crate.rs | 2 +- tests/ui/eii/linking/same-symbol.rs | 2 +- tests/ui/eii/shadow_builtin.rs | 17 +++++++++ tests/ui/eii/shadow_builtin.stderr | 26 ++++++++++++++ tests/ui/eii/static/argument_required.rs | 11 ++++++ tests/ui/eii/static/argument_required.stderr | 8 +++++ .../eii/static/auxiliary/cross_crate_decl.rs | 6 ++++ .../eii/static/auxiliary/cross_crate_def.rs | 9 +++++ tests/ui/eii/static/cross_crate_decl.rs | 21 +++++++++++ .../ui/eii/static/cross_crate_decl.run.stdout | 2 ++ tests/ui/eii/static/cross_crate_def.rs | 18 ++++++++++ .../ui/eii/static/cross_crate_def.run.stdout | 2 ++ tests/ui/eii/static/duplicate.rs | 25 +++++++++++++ tests/ui/eii/static/duplicate.stderr | 13 +++++++ tests/ui/eii/static/mismatch_fn_static.rs | 14 ++++++++ tests/ui/eii/static/mismatch_fn_static.stderr | 8 +++++ tests/ui/eii/static/mismatch_mut.rs | 21 +++++++++++ tests/ui/eii/static/mismatch_mut.stderr | 8 +++++ tests/ui/eii/static/mismatch_safety.rs | 21 +++++++++++ tests/ui/eii/static/mismatch_safety.stderr | 8 +++++ tests/ui/eii/static/mismatch_static_fn.rs | 16 +++++++++ tests/ui/eii/static/mismatch_static_fn.stderr | 8 +++++ tests/ui/eii/static/multiple_impls.rs | 21 +++++++++++ tests/ui/eii/static/multiple_impls.run.stdout | 1 + tests/ui/eii/static/mut.rs | 22 ++++++++++++ tests/ui/eii/static/mut.run.stdout | 2 ++ tests/ui/eii/static/same_address.rs | 21 +++++++++++ tests/ui/eii/static/simple.rs | 22 ++++++++++++ tests/ui/eii/static/simple.run.stdout | 2 ++ tests/ui/eii/static/subtype.rs | 18 ++++++++++ tests/ui/eii/static/subtype_wrong.rs | 17 +++++++++ tests/ui/eii/static/subtype_wrong.stderr | 12 +++++++ tests/ui/eii/static/wrong_ty.rs | 18 ++++++++++ tests/ui/eii/static/wrong_ty.stderr | 15 ++++++++ 48 files changed, 473 insertions(+), 48 deletions(-) create mode 100644 tests/ui/eii/shadow_builtin.rs create mode 100644 tests/ui/eii/shadow_builtin.stderr create mode 100644 tests/ui/eii/static/argument_required.rs create mode 100644 tests/ui/eii/static/argument_required.stderr create mode 100644 tests/ui/eii/static/auxiliary/cross_crate_decl.rs create mode 100644 tests/ui/eii/static/auxiliary/cross_crate_def.rs create mode 100644 tests/ui/eii/static/cross_crate_decl.rs create mode 100644 tests/ui/eii/static/cross_crate_decl.run.stdout create mode 100644 tests/ui/eii/static/cross_crate_def.rs create mode 100644 tests/ui/eii/static/cross_crate_def.run.stdout create mode 100644 tests/ui/eii/static/duplicate.rs create mode 100644 tests/ui/eii/static/duplicate.stderr create mode 100644 tests/ui/eii/static/mismatch_fn_static.rs create mode 100644 tests/ui/eii/static/mismatch_fn_static.stderr create mode 100644 tests/ui/eii/static/mismatch_mut.rs create mode 100644 tests/ui/eii/static/mismatch_mut.stderr create mode 100644 tests/ui/eii/static/mismatch_safety.rs create mode 100644 tests/ui/eii/static/mismatch_safety.stderr create mode 100644 tests/ui/eii/static/mismatch_static_fn.rs create mode 100644 tests/ui/eii/static/mismatch_static_fn.stderr create mode 100644 tests/ui/eii/static/multiple_impls.rs create mode 100644 tests/ui/eii/static/multiple_impls.run.stdout create mode 100644 tests/ui/eii/static/mut.rs create mode 100644 tests/ui/eii/static/mut.run.stdout create mode 100644 tests/ui/eii/static/same_address.rs create mode 100644 tests/ui/eii/static/simple.rs create mode 100644 tests/ui/eii/static/simple.run.stdout create mode 100644 tests/ui/eii/static/subtype.rs create mode 100644 tests/ui/eii/static/subtype_wrong.rs create mode 100644 tests/ui/eii/static/subtype_wrong.stderr create mode 100644 tests/ui/eii/static/wrong_ty.rs create mode 100644 tests/ui/eii/static/wrong_ty.stderr diff --git a/tests/ui/eii/attribute_targets.stderr b/tests/ui/eii/attribute_targets.stderr index bf04c323c95ca..8166609e19740 100644 --- a/tests/ui/eii/attribute_targets.stderr +++ b/tests/ui/eii/attribute_targets.stderr @@ -1,106 +1,106 @@ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:7:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:9:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:13:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:15:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:21:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:23:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:27:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:29:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:32:5 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:34:5 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:39:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:41:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:44:5 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:46:5 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:51:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:53:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:56:5 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:58:5 | LL | #[eii] diff --git a/tests/ui/eii/default/call_default.rs b/tests/ui/eii/default/call_default.rs index b60a1dd0b2156..8806c7fa7d8ce 100644 --- a/tests/ui/eii/default/call_default.rs +++ b/tests/ui/eii/default/call_default.rs @@ -3,7 +3,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests EIIs with default implementations. // When there's no explicit declaration, the default should be called from the declaring crate. diff --git a/tests/ui/eii/default/call_default_panics.rs b/tests/ui/eii/default/call_default_panics.rs index 96b2742aa8e0a..db664e0cbcb0d 100644 --- a/tests/ui/eii/default/call_default_panics.rs +++ b/tests/ui/eii/default/call_default_panics.rs @@ -5,7 +5,7 @@ //@ needs-unwind //@ exec-env:RUST_BACKTRACE=1 //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // A small test to make sure that unwinding works properly. // diff --git a/tests/ui/eii/default/call_impl.rs b/tests/ui/eii/default/call_impl.rs index b887697574892..1a972774beaeb 100644 --- a/tests/ui/eii/default/call_impl.rs +++ b/tests/ui/eii/default/call_impl.rs @@ -4,7 +4,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests EIIs with default implementations. // When an explicit implementation is given in one dependency, and the declaration is in another, diff --git a/tests/ui/eii/default/local_crate.rs b/tests/ui/eii/default/local_crate.rs index d98c2fac4234e..fd4fd459c52fb 100644 --- a/tests/ui/eii/default/local_crate.rs +++ b/tests/ui/eii/default/local_crate.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests EIIs with default implementations. // In the same crate, when there's no explicit declaration, the default should be called. diff --git a/tests/ui/eii/default/local_crate_explicit.rs b/tests/ui/eii/default/local_crate_explicit.rs index a4cc54fcd31fc..200905b8753a0 100644 --- a/tests/ui/eii/default/local_crate_explicit.rs +++ b/tests/ui/eii/default/local_crate_explicit.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests EIIs with default implementations. // In the same crate, the explicit implementation should get priority. diff --git a/tests/ui/eii/duplicate/duplicate1.rs b/tests/ui/eii/duplicate/duplicate1.rs index 3269778aca0c4..2128cac70eb30 100644 --- a/tests/ui/eii/duplicate/duplicate1.rs +++ b/tests/ui/eii/duplicate/duplicate1.rs @@ -2,7 +2,7 @@ //@ aux-build: impl1.rs //@ aux-build: impl2.rs //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // tests that EIIs error properly, even if the conflicting implementations live in another crate. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/duplicate2.rs b/tests/ui/eii/duplicate/duplicate2.rs index 4c883d28d74a4..b0f1b1266e4ca 100644 --- a/tests/ui/eii/duplicate/duplicate2.rs +++ b/tests/ui/eii/duplicate/duplicate2.rs @@ -3,7 +3,7 @@ //@ aux-build: impl2.rs //@ aux-build: impl3.rs //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests the error message when there are multiple implementations of an EII in many crates. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/duplicate3.rs b/tests/ui/eii/duplicate/duplicate3.rs index b046760745097..4b2b0fc111b58 100644 --- a/tests/ui/eii/duplicate/duplicate3.rs +++ b/tests/ui/eii/duplicate/duplicate3.rs @@ -4,7 +4,7 @@ //@ aux-build: impl3.rs //@ aux-build: impl4.rs //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests the error message when there are multiple implementations of an EII in many crates. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/multiple_impls.rs b/tests/ui/eii/duplicate/multiple_impls.rs index c02c783223ac3..5ce2a27e16957 100644 --- a/tests/ui/eii/duplicate/multiple_impls.rs +++ b/tests/ui/eii/duplicate/multiple_impls.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests whether one function could implement two EIIs. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/error_statement_position.stderr b/tests/ui/eii/error_statement_position.stderr index f14e6c33e64f5..c6af18044db8d 100644 --- a/tests/ui/eii/error_statement_position.stderr +++ b/tests/ui/eii/error_statement_position.stderr @@ -1,4 +1,4 @@ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/error_statement_position.rs:8:5 | LL | #[eii] diff --git a/tests/ui/eii/errors.rs b/tests/ui/eii/errors.rs index 5fcf33336b402..bc6c17f463a78 100644 --- a/tests/ui/eii/errors.rs +++ b/tests/ui/eii/errors.rs @@ -25,11 +25,9 @@ unsafe extern "Rust" { safe fn bar(x: u64) -> u64; } -#[foo] //~ ERROR `#[foo]` is only valid on functions -static X: u64 = 4; -#[foo] //~ ERROR `#[foo]` is only valid on functions +#[foo] //~ ERROR `#[foo]` is only valid on functions and statics const Y: u64 = 4; -#[foo] //~ ERROR `#[foo]` is only valid on functions +#[foo] //~ ERROR `#[foo]` is only valid on functions and statics macro bar() {} #[foo()] diff --git a/tests/ui/eii/errors.stderr b/tests/ui/eii/errors.stderr index c7eb96a9c2190..553ae622cb36f 100644 --- a/tests/ui/eii/errors.stderr +++ b/tests/ui/eii/errors.stderr @@ -52,47 +52,41 @@ error: `#[eii_declaration(...)]` expects a list of one or two elements LL | #[eii_declaration = "unsafe"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/errors.rs:28:1 | LL | #[foo] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/errors.rs:30:1 | LL | #[foo] | ^^^^^^ -error: `#[foo]` is only valid on functions - --> $DIR/errors.rs:32:1 - | -LL | #[foo] - | ^^^^^^ - error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` - --> $DIR/errors.rs:35:1 + --> $DIR/errors.rs:33:1 | LL | #[foo()] | ^^^^^^^^ error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` - --> $DIR/errors.rs:37:1 + --> $DIR/errors.rs:35:1 | LL | #[foo(default, bar)] | ^^^^^^^^^^^^^^^^^^^^ error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` - --> $DIR/errors.rs:39:1 + --> $DIR/errors.rs:37:1 | LL | #[foo("default")] | ^^^^^^^^^^^^^^^^^ error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` - --> $DIR/errors.rs:41:1 + --> $DIR/errors.rs:39:1 | LL | #[foo = "default"] | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/eii/linking/codegen_cross_crate.rs b/tests/ui/eii/linking/codegen_cross_crate.rs index 2958a0f10521b..192aac5920704 100644 --- a/tests/ui/eii/linking/codegen_cross_crate.rs +++ b/tests/ui/eii/linking/codegen_cross_crate.rs @@ -3,7 +3,7 @@ //@ aux-build: codegen_cross_crate_other_crate.rs //@ compile-flags: -O //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests whether calling EIIs works with the declaration in another crate. diff --git a/tests/ui/eii/linking/codegen_single_crate.rs b/tests/ui/eii/linking/codegen_single_crate.rs index 8e85c354bba1c..d0e9c015da418 100644 --- a/tests/ui/eii/linking/codegen_single_crate.rs +++ b/tests/ui/eii/linking/codegen_single_crate.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests whether calling EIIs works with the declaration in the same crate. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/linking/same-symbol.rs b/tests/ui/eii/linking/same-symbol.rs index baf36ff4f5a0a..afba9b7750262 100644 --- a/tests/ui/eii/linking/same-symbol.rs +++ b/tests/ui/eii/linking/same-symbol.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows #![feature(extern_item_impls)] diff --git a/tests/ui/eii/shadow_builtin.rs b/tests/ui/eii/shadow_builtin.rs new file mode 100644 index 0000000000000..5f619b79d01a4 --- /dev/null +++ b/tests/ui/eii/shadow_builtin.rs @@ -0,0 +1,17 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether calling EIIs works with the declaration in the same crate. +#![feature(extern_item_impls)] + +#[eii(inline)] +//~^ ERROR `#[inline]` required, but not found +fn test(x: u64); + +#[inline] +//~^ ERROR `inline` is ambiguous +fn test_impl(x: u64) { + println!("{x:?}") +} + +fn main() { } diff --git a/tests/ui/eii/shadow_builtin.stderr b/tests/ui/eii/shadow_builtin.stderr new file mode 100644 index 0000000000000..d48e66a18af20 --- /dev/null +++ b/tests/ui/eii/shadow_builtin.stderr @@ -0,0 +1,26 @@ +error[E0659]: `inline` is ambiguous + --> $DIR/shadow_builtin.rs:11:3 + | +LL | #[inline] + | ^^^^^^ ambiguous name + | + = note: ambiguous because of a name conflict with a builtin attribute + = note: `inline` could refer to a built-in attribute +note: `inline` could also refer to the attribute macro defined here + --> $DIR/shadow_builtin.rs:7:1 + | +LL | #[eii(inline)] + | ^^^^^^^^^^^^^^ + = help: use `crate::inline` to refer to this attribute macro unambiguously + +error: `#[inline]` required, but not found + --> $DIR/shadow_builtin.rs:7:7 + | +LL | #[eii(inline)] + | ^^^^^^ expected because `#[inline]` was declared here in crate `shadow_builtin` + | + = help: expected at least one implementation in crate `shadow_builtin` or any of its dependencies + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0659`. diff --git a/tests/ui/eii/static/argument_required.rs b/tests/ui/eii/static/argument_required.rs new file mode 100644 index 0000000000000..114b8a35de5cf --- /dev/null +++ b/tests/ui/eii/static/argument_required.rs @@ -0,0 +1,11 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii] +//~^ ERROR `#[eii]` requires the name as an explicit argument when used on a static +static HELLO: u64; + +fn main() { } diff --git a/tests/ui/eii/static/argument_required.stderr b/tests/ui/eii/static/argument_required.stderr new file mode 100644 index 0000000000000..9e5ee398f77d8 --- /dev/null +++ b/tests/ui/eii/static/argument_required.stderr @@ -0,0 +1,8 @@ +error: `#[eii]` requires the name as an explicit argument when used on a static + --> $DIR/argument_required.rs:7:1 + | +LL | #[eii] + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/auxiliary/cross_crate_decl.rs b/tests/ui/eii/static/auxiliary/cross_crate_decl.rs new file mode 100644 index 0000000000000..06b7daca22074 --- /dev/null +++ b/tests/ui/eii/static/auxiliary/cross_crate_decl.rs @@ -0,0 +1,6 @@ +//@ no-prefer-dynamic +#![crate_type = "rlib"] +#![feature(extern_item_impls)] + +#[eii(eii1)] +pub static DECL1: u64; diff --git a/tests/ui/eii/static/auxiliary/cross_crate_def.rs b/tests/ui/eii/static/auxiliary/cross_crate_def.rs new file mode 100644 index 0000000000000..70933440a62be --- /dev/null +++ b/tests/ui/eii/static/auxiliary/cross_crate_def.rs @@ -0,0 +1,9 @@ +//@ no-prefer-dynamic +#![crate_type = "rlib"] +#![feature(extern_item_impls)] + +#[eii(eii1)] +pub static DECL1: u64; + +#[eii1] +pub static EII1_IMPL: u64 = 5; diff --git a/tests/ui/eii/static/cross_crate_decl.rs b/tests/ui/eii/static/cross_crate_decl.rs new file mode 100644 index 0000000000000..63e3511198e1d --- /dev/null +++ b/tests/ui/eii/static/cross_crate_decl.rs @@ -0,0 +1,21 @@ +//@ run-pass +//@ check-run-results +//@ aux-build: cross_crate_decl.rs +//@ compile-flags: -O +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether calling EIIs works with the declaration in another crate. + +extern crate cross_crate_decl as codegen; + +#[codegen::eii1] +static EII1_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{}", EII1_IMPL); + // through the alias + println!("{}", codegen::DECL1); +} diff --git a/tests/ui/eii/static/cross_crate_decl.run.stdout b/tests/ui/eii/static/cross_crate_decl.run.stdout new file mode 100644 index 0000000000000..fd3c81a4d7631 --- /dev/null +++ b/tests/ui/eii/static/cross_crate_decl.run.stdout @@ -0,0 +1,2 @@ +5 +5 diff --git a/tests/ui/eii/static/cross_crate_def.rs b/tests/ui/eii/static/cross_crate_def.rs new file mode 100644 index 0000000000000..a0b6afbfd760b --- /dev/null +++ b/tests/ui/eii/static/cross_crate_def.rs @@ -0,0 +1,18 @@ +//@ run-pass +//@ check-run-results +//@ aux-build: cross_crate_def.rs +//@ compile-flags: -O +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether calling EIIs works with the declaration and definition in another crate. + +extern crate cross_crate_def as codegen; + +// what you would write: +fn main() { + // directly + println!("{}", codegen::EII1_IMPL); + // through the alias + println!("{}", codegen::DECL1); +} diff --git a/tests/ui/eii/static/cross_crate_def.run.stdout b/tests/ui/eii/static/cross_crate_def.run.stdout new file mode 100644 index 0000000000000..fd3c81a4d7631 --- /dev/null +++ b/tests/ui/eii/static/cross_crate_def.run.stdout @@ -0,0 +1,2 @@ +5 +5 diff --git a/tests/ui/eii/static/duplicate.rs b/tests/ui/eii/static/duplicate.rs new file mode 100644 index 0000000000000..12b2e56c07e4e --- /dev/null +++ b/tests/ui/eii/static/duplicate.rs @@ -0,0 +1,25 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +static HELLO_IMPL1: u64 = 5; +//~^ ERROR multiple implementations of `#[hello]` + +#[hello] +static HELLO_IMPL2: u64 = 6; + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL1}"); + println!("{HELLO_IMPL2}"); + + // through the alias + println!("{HELLO}"); +} diff --git a/tests/ui/eii/static/duplicate.stderr b/tests/ui/eii/static/duplicate.stderr new file mode 100644 index 0000000000000..270664c8c74c6 --- /dev/null +++ b/tests/ui/eii/static/duplicate.stderr @@ -0,0 +1,13 @@ +error: multiple implementations of `#[hello]` + --> $DIR/duplicate.rs:11:1 + | +LL | static HELLO_IMPL1: u64 = 5; + | ^^^^^^^^^^^^^^^^^^^^^^^ first implemented here in crate `duplicate` +... +LL | static HELLO_IMPL2: u64 = 6; + | ----------------------- also implemented here in crate `duplicate` + | + = help: an "externally implementable item" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_fn_static.rs b/tests/ui/eii/static/mismatch_fn_static.rs new file mode 100644 index 0000000000000..298fdca18d967 --- /dev/null +++ b/tests/ui/eii/static/mismatch_fn_static.rs @@ -0,0 +1,14 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +fn hello() -> u64; + +#[hello] +//~^ ERROR `#[hello]` must be used on a function +static HELLO_IMPL: u64 = 5; + +fn main() { } diff --git a/tests/ui/eii/static/mismatch_fn_static.stderr b/tests/ui/eii/static/mismatch_fn_static.stderr new file mode 100644 index 0000000000000..e8fa5f85b1f13 --- /dev/null +++ b/tests/ui/eii/static/mismatch_fn_static.stderr @@ -0,0 +1,8 @@ +error: `#[hello]` must be used on a function + --> $DIR/mismatch_fn_static.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_mut.rs b/tests/ui/eii/static/mismatch_mut.rs new file mode 100644 index 0000000000000..e99c948d0dccf --- /dev/null +++ b/tests/ui/eii/static/mismatch_mut.rs @@ -0,0 +1,21 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static mut HELLO: u64; + +#[hello] +//~^ ERROR mutability does not match with the definition of`#[hello]` +static HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL}"); + + // through the alias + println!("{}", unsafe { HELLO }); +} diff --git a/tests/ui/eii/static/mismatch_mut.stderr b/tests/ui/eii/static/mismatch_mut.stderr new file mode 100644 index 0000000000000..a8438789c4049 --- /dev/null +++ b/tests/ui/eii/static/mismatch_mut.stderr @@ -0,0 +1,8 @@ +error: mutability does not match with the definition of`#[hello]` + --> $DIR/mismatch_mut.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_safety.rs b/tests/ui/eii/static/mismatch_safety.rs new file mode 100644 index 0000000000000..9579cd68cb84b --- /dev/null +++ b/tests/ui/eii/static/mismatch_safety.rs @@ -0,0 +1,21 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +unsafe static mut HELLO: u64; + +#[hello] +//~^ ERROR safety does not match with the definition of`#[hello]` +static HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL}"); + + // through the alias + println!("{}", unsafe { HELLO }); +} diff --git a/tests/ui/eii/static/mismatch_safety.stderr b/tests/ui/eii/static/mismatch_safety.stderr new file mode 100644 index 0000000000000..d4fa85778ae11 --- /dev/null +++ b/tests/ui/eii/static/mismatch_safety.stderr @@ -0,0 +1,8 @@ +error: safety does not match with the definition of`#[hello]` + --> $DIR/mismatch_safety.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_static_fn.rs b/tests/ui/eii/static/mismatch_static_fn.rs new file mode 100644 index 0000000000000..cd9a8109dc339 --- /dev/null +++ b/tests/ui/eii/static/mismatch_static_fn.rs @@ -0,0 +1,16 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +//~^ ERROR `#[hello]` must be used on a static +fn hello_impl() -> u64 { + 5 +} + +fn main() { } diff --git a/tests/ui/eii/static/mismatch_static_fn.stderr b/tests/ui/eii/static/mismatch_static_fn.stderr new file mode 100644 index 0000000000000..639e3cfa3beb3 --- /dev/null +++ b/tests/ui/eii/static/mismatch_static_fn.stderr @@ -0,0 +1,8 @@ +error: `#[hello]` must be used on a static + --> $DIR/mismatch_static_fn.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/multiple_impls.rs b/tests/ui/eii/static/multiple_impls.rs new file mode 100644 index 0000000000000..81adf22680f7f --- /dev/null +++ b/tests/ui/eii/static/multiple_impls.rs @@ -0,0 +1,21 @@ +//@ run-pass +//@ check-run-results +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether one function could implement two EIIs. +#![feature(extern_item_impls)] + +#[eii(a)] +static A: u64; + +#[eii(b)] +static B: u64; + +#[a] +#[b] +static IMPL: u64 = 5; + +fn main() { + println!("{A} {B} {IMPL}") +} diff --git a/tests/ui/eii/static/multiple_impls.run.stdout b/tests/ui/eii/static/multiple_impls.run.stdout new file mode 100644 index 0000000000000..58945c2b48291 --- /dev/null +++ b/tests/ui/eii/static/multiple_impls.run.stdout @@ -0,0 +1 @@ +5 5 5 diff --git a/tests/ui/eii/static/mut.rs b/tests/ui/eii/static/mut.rs new file mode 100644 index 0000000000000..49c36f72ab86f --- /dev/null +++ b/tests/ui/eii/static/mut.rs @@ -0,0 +1,22 @@ +//@ run-pass +//@ check-run-results +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static mut HELLO: u64; + +#[hello] +static mut HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{}", unsafe { HELLO_IMPL }); + + // through the alias + println!("{}", unsafe { HELLO }); +} diff --git a/tests/ui/eii/static/mut.run.stdout b/tests/ui/eii/static/mut.run.stdout new file mode 100644 index 0000000000000..fd3c81a4d7631 --- /dev/null +++ b/tests/ui/eii/static/mut.run.stdout @@ -0,0 +1,2 @@ +5 +5 diff --git a/tests/ui/eii/static/same_address.rs b/tests/ui/eii/static/same_address.rs new file mode 100644 index 0000000000000..81de19406dc49 --- /dev/null +++ b/tests/ui/eii/static/same_address.rs @@ -0,0 +1,21 @@ +//@ run-pass +//@ check-run-results +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs and their declarations share the same address +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +static HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + assert_eq!( + &HELLO as *const u64 as usize, + &HELLO_IMPL as *const u64 as usize, + ) +} diff --git a/tests/ui/eii/static/simple.rs b/tests/ui/eii/static/simple.rs new file mode 100644 index 0000000000000..661ab9b9835f2 --- /dev/null +++ b/tests/ui/eii/static/simple.rs @@ -0,0 +1,22 @@ +//@ run-pass +//@ check-run-results +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +static HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL}"); + + // through the alias + println!("{HELLO}"); +} diff --git a/tests/ui/eii/static/simple.run.stdout b/tests/ui/eii/static/simple.run.stdout new file mode 100644 index 0000000000000..fd3c81a4d7631 --- /dev/null +++ b/tests/ui/eii/static/simple.run.stdout @@ -0,0 +1,2 @@ +5 +5 diff --git a/tests/ui/eii/static/subtype.rs b/tests/ui/eii/static/subtype.rs new file mode 100644 index 0000000000000..d98e94fa90322 --- /dev/null +++ b/tests/ui/eii/static/subtype.rs @@ -0,0 +1,18 @@ +//@ check-pass +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests that mismatching types of the declaration and definition are rejected +#![feature(extern_item_impls)] + +use std::ptr; + +#[eii(hello)] +static HELLO: for<'a> fn(&'a u8) -> &'a u8; + +#[hello] +static HELLO_IMPL: for<'a> fn(&'a u8) -> &'static u8 = |_| todo!(); + +fn main() { + +} diff --git a/tests/ui/eii/static/subtype_wrong.rs b/tests/ui/eii/static/subtype_wrong.rs new file mode 100644 index 0000000000000..964a3d767b197 --- /dev/null +++ b/tests/ui/eii/static/subtype_wrong.rs @@ -0,0 +1,17 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests that mismatching types of the declaration and definition are rejected +#![feature(extern_item_impls)] + +use std::ptr; + +#[eii(hello)] +static HELLO: for<'a> fn(&'a u8) -> &'static u8; + +#[hello] +static HELLO_IMPL: for<'a> fn(&'a u8) -> &'a u8 = |_| todo!(); +//~^ ERROR mismatched types + +fn main() { +} diff --git a/tests/ui/eii/static/subtype_wrong.stderr b/tests/ui/eii/static/subtype_wrong.stderr new file mode 100644 index 0000000000000..a20074947c15d --- /dev/null +++ b/tests/ui/eii/static/subtype_wrong.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/subtype_wrong.rs:13:1 + | +LL | static HELLO_IMPL: for<'a> fn(&'a u8) -> &'a u8 = |_| todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected fn pointer `for<'a> fn(&'a _) -> &'static _` + found fn pointer `for<'a> fn(&'a _) -> &'a _` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/eii/static/wrong_ty.rs b/tests/ui/eii/static/wrong_ty.rs new file mode 100644 index 0000000000000..beee0a5a0857b --- /dev/null +++ b/tests/ui/eii/static/wrong_ty.rs @@ -0,0 +1,18 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests that mismatching types of the declaration and definition are rejected +#![feature(extern_item_impls)] + +use std::ptr; + +#[eii(hello)] +static HELLO: u64; + +#[hello] +static HELLO_IMPL: bool = true; +//~^ ERROR static `HELLO_IMPL` has a type that is incompatible with the declaration of `#[hello]` [E0806] + +fn main() { + +} diff --git a/tests/ui/eii/static/wrong_ty.stderr b/tests/ui/eii/static/wrong_ty.stderr new file mode 100644 index 0000000000000..5095513527747 --- /dev/null +++ b/tests/ui/eii/static/wrong_ty.stderr @@ -0,0 +1,15 @@ +error[E0806]: static `HELLO_IMPL` has a type that is incompatible with the declaration of `#[hello]` + --> $DIR/wrong_ty.rs:13:1 + | +LL | static HELLO_IMPL: bool = true; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: expected this because of this attribute + --> $DIR/wrong_ty.rs:12:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0806`. From 59ed2459457eb7c95b2baa9dccdb74f63b9c0a80 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 10 Apr 2026 15:05:07 +0200 Subject: [PATCH 14/33] Reject mutable externally implementable statics --- compiler/rustc_builtin_macros/src/eii.rs | 14 ++++++++++++-- compiler/rustc_builtin_macros/src/errors.rs | 8 ++++++++ tests/ui/eii/static/mismatch_mut.rs | 1 + tests/ui/eii/static/mismatch_mut.stderr | 10 ++++++++-- tests/ui/eii/static/mismatch_mut2.rs | 21 +++++++++++++++++++++ tests/ui/eii/static/mismatch_mut2.stderr | 8 ++++++++ tests/ui/eii/static/mismatch_safety.rs | 2 +- tests/ui/eii/static/mismatch_safety2.rs | 21 +++++++++++++++++++++ tests/ui/eii/static/mismatch_safety2.stderr | 8 ++++++++ tests/ui/eii/static/mut.rs | 3 +-- tests/ui/eii/static/mut.stderr | 8 ++++++++ 11 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 tests/ui/eii/static/mismatch_mut2.rs create mode 100644 tests/ui/eii/static/mismatch_mut2.stderr create mode 100644 tests/ui/eii/static/mismatch_safety2.rs create mode 100644 tests/ui/eii/static/mismatch_safety2.stderr create mode 100644 tests/ui/eii/static/mut.stderr diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 9dab90b72a027..0cf9adfd4bcca 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -1,7 +1,8 @@ use rustc_ast::token::{Delimiter, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::{ - Attribute, DUMMY_NODE_ID, EiiDecl, EiiImpl, ItemKind, MetaItem, Path, StmtKind, Visibility, ast, + Attribute, DUMMY_NODE_ID, EiiDecl, EiiImpl, ItemKind, MetaItem, Mutability, Path, StmtKind, + Visibility, ast, }; use rustc_ast_pretty::pprust::path_to_string; use rustc_expand::base::{Annotatable, ExtCtxt}; @@ -11,7 +12,7 @@ use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, - EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, + EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, EiiStaticMutable, }; /// ```rust @@ -100,6 +101,15 @@ fn eii_( }); return vec![]; } + + // Mut statics are currently not supported + if stat.mutability == Mutability::Mut { + ecx.dcx().emit_err(EiiStaticMutable { + span: eii_attr_span, + name: path_to_string(&meta_item.path), + }); + } + (item.span, stat.ident) } _ => { diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 4b572c2f34ac0..b210f93338d31 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1140,6 +1140,14 @@ pub(crate) struct EiiStaticArgumentRequired { pub name: String, } +#[derive(Diagnostic)] +#[diag("`#[{$name}]` cannot be used on mutable statics")] +pub(crate) struct EiiStaticMutable { + #[primary_span] + pub span: Span, + pub name: String, +} + #[derive(Diagnostic)] #[diag("`#[{$name}]` can only be used on functions inside a module")] pub(crate) struct EiiSharedMacroInStatementPosition { diff --git a/tests/ui/eii/static/mismatch_mut.rs b/tests/ui/eii/static/mismatch_mut.rs index e99c948d0dccf..87c2c4128aa5e 100644 --- a/tests/ui/eii/static/mismatch_mut.rs +++ b/tests/ui/eii/static/mismatch_mut.rs @@ -5,6 +5,7 @@ #![feature(extern_item_impls)] #[eii(hello)] +//~^ ERROR `#[eii]` cannot be used on mutable statics static mut HELLO: u64; #[hello] diff --git a/tests/ui/eii/static/mismatch_mut.stderr b/tests/ui/eii/static/mismatch_mut.stderr index a8438789c4049..537ac0de3c3a2 100644 --- a/tests/ui/eii/static/mismatch_mut.stderr +++ b/tests/ui/eii/static/mismatch_mut.stderr @@ -1,8 +1,14 @@ +error: `#[eii]` cannot be used on mutable statics + --> $DIR/mismatch_mut.rs:7:1 + | +LL | #[eii(hello)] + | ^^^^^^^^^^^^^ + error: mutability does not match with the definition of`#[hello]` - --> $DIR/mismatch_mut.rs:10:1 + --> $DIR/mismatch_mut.rs:11:1 | LL | #[hello] | ^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/eii/static/mismatch_mut2.rs b/tests/ui/eii/static/mismatch_mut2.rs new file mode 100644 index 0000000000000..ab525e418adeb --- /dev/null +++ b/tests/ui/eii/static/mismatch_mut2.rs @@ -0,0 +1,21 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +//~^ ERROR mutability does not match with the definition of`#[hello]` +static mut HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{}", unsafe { HELLO_IMPL }); + + // through the alias + println!("{HELLO}"); +} diff --git a/tests/ui/eii/static/mismatch_mut2.stderr b/tests/ui/eii/static/mismatch_mut2.stderr new file mode 100644 index 0000000000000..6ac3df57697db --- /dev/null +++ b/tests/ui/eii/static/mismatch_mut2.stderr @@ -0,0 +1,8 @@ +error: mutability does not match with the definition of`#[hello]` + --> $DIR/mismatch_mut2.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_safety.rs b/tests/ui/eii/static/mismatch_safety.rs index 9579cd68cb84b..f30326b0755c0 100644 --- a/tests/ui/eii/static/mismatch_safety.rs +++ b/tests/ui/eii/static/mismatch_safety.rs @@ -5,7 +5,7 @@ #![feature(extern_item_impls)] #[eii(hello)] -unsafe static mut HELLO: u64; +unsafe static HELLO: u64; #[hello] //~^ ERROR safety does not match with the definition of`#[hello]` diff --git a/tests/ui/eii/static/mismatch_safety2.rs b/tests/ui/eii/static/mismatch_safety2.rs new file mode 100644 index 0000000000000..dea45c26292d8 --- /dev/null +++ b/tests/ui/eii/static/mismatch_safety2.rs @@ -0,0 +1,21 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +unsafe static HELLO_IMPL: u64 = 5; +//~^ ERROR static items cannot be declared with `unsafe` safety qualifier outside of `extern` block + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL}"); + + // through the alias + println!("{}", unsafe { HELLO }); +} diff --git a/tests/ui/eii/static/mismatch_safety2.stderr b/tests/ui/eii/static/mismatch_safety2.stderr new file mode 100644 index 0000000000000..6957a6202b614 --- /dev/null +++ b/tests/ui/eii/static/mismatch_safety2.stderr @@ -0,0 +1,8 @@ +error: static items cannot be declared with `unsafe` safety qualifier outside of `extern` block + --> $DIR/mismatch_safety2.rs:11:1 + | +LL | unsafe static HELLO_IMPL: u64 = 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mut.rs b/tests/ui/eii/static/mut.rs index 49c36f72ab86f..803ffc2297992 100644 --- a/tests/ui/eii/static/mut.rs +++ b/tests/ui/eii/static/mut.rs @@ -1,5 +1,3 @@ -//@ run-pass -//@ check-run-results //@ ignore-backends: gcc // FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows @@ -7,6 +5,7 @@ #![feature(extern_item_impls)] #[eii(hello)] +//~^ ERROR `#[eii]` cannot be used on mutable statics static mut HELLO: u64; #[hello] diff --git a/tests/ui/eii/static/mut.stderr b/tests/ui/eii/static/mut.stderr new file mode 100644 index 0000000000000..cd3a0ca23c7f4 --- /dev/null +++ b/tests/ui/eii/static/mut.stderr @@ -0,0 +1,8 @@ +error: `#[eii]` cannot be used on mutable statics + --> $DIR/mut.rs:7:1 + | +LL | #[eii(hello)] + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 1f2b0900a702d4947d8f11ea7b162c3af1626e3c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 10 Apr 2026 15:26:11 +0200 Subject: [PATCH 15/33] Reject multiple EII implementations on one static --- compiler/rustc_builtin_macros/src/eii.rs | 12 ++++++++++-- compiler/rustc_builtin_macros/src/errors.rs | 10 ++++++++++ tests/ui/eii/static/multiple_decls.rs | 11 +++++++++++ tests/ui/eii/static/multiple_decls.stderr | 14 ++++++++++++++ tests/ui/eii/static/multiple_impls.rs | 3 +-- tests/ui/eii/static/multiple_impls.stderr | 10 ++++++++++ 6 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 tests/ui/eii/static/multiple_decls.rs create mode 100644 tests/ui/eii/static/multiple_decls.stderr create mode 100644 tests/ui/eii/static/multiple_impls.stderr diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 0cf9adfd4bcca..fd0ef8500c6c3 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -12,7 +12,8 @@ use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, - EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, EiiStaticMutable, + EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, + EiiStaticMultipleImplementations, EiiStaticMutable, }; /// ```rust @@ -512,7 +513,14 @@ pub(crate) fn eii_shared_macro( let eii_impls = match &mut i.kind { ItemKind::Fn(func) => &mut func.eii_impls, - ItemKind::Static(stat) => &mut stat.eii_impls, + ItemKind::Static(stat) => { + if !stat.eii_impls.is_empty() { + // Reject multiple implementations on one static item + // because it might be unintuitive for libraries defining statics the defined statics may alias + ecx.dcx().emit_err(EiiStaticMultipleImplementations { span }); + } + &mut stat.eii_impls + } _ => { ecx.dcx() .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) }); diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index b210f93338d31..ad641beb87d98 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1124,6 +1124,16 @@ pub(crate) struct EiiSharedMacroTarget { pub name: String, } +#[derive(Diagnostic)] +#[diag("static cannot implement multiple EIIs")] +#[note( + "this is not allowed because multiple externally implementable statics that alias may be unintuitive" +)] +pub(crate) struct EiiStaticMultipleImplementations { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`#[{$name}]` cannot be used on statics with a value")] pub(crate) struct EiiStaticDefault { diff --git a/tests/ui/eii/static/multiple_decls.rs b/tests/ui/eii/static/multiple_decls.rs new file mode 100644 index 0000000000000..1913dc39e8b7c --- /dev/null +++ b/tests/ui/eii/static/multiple_decls.rs @@ -0,0 +1,11 @@ +#![feature(extern_item_impls)] + +const A: () = (); +#[eii(A)] +static A: u64; +//~^ ERROR the name `A` is defined multiple times + +#[A] +static A_IMPL: u64 = 5; + +fn main() {} diff --git a/tests/ui/eii/static/multiple_decls.stderr b/tests/ui/eii/static/multiple_decls.stderr new file mode 100644 index 0000000000000..b0b5da1aabe1b --- /dev/null +++ b/tests/ui/eii/static/multiple_decls.stderr @@ -0,0 +1,14 @@ +error[E0428]: the name `A` is defined multiple times + --> $DIR/multiple_decls.rs:5:1 + | +LL | const A: () = (); + | ----------------- previous definition of the value `A` here +LL | #[eii(A)] +LL | static A: u64; + | ^^^^^^^^^^^^^^ `A` redefined here + | + = note: `A` must be defined only once in the value namespace of this module + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0428`. diff --git a/tests/ui/eii/static/multiple_impls.rs b/tests/ui/eii/static/multiple_impls.rs index 81adf22680f7f..8ad7d87040a36 100644 --- a/tests/ui/eii/static/multiple_impls.rs +++ b/tests/ui/eii/static/multiple_impls.rs @@ -1,5 +1,3 @@ -//@ run-pass -//@ check-run-results //@ ignore-backends: gcc // FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows @@ -14,6 +12,7 @@ static B: u64; #[a] #[b] +//~^ ERROR static cannot implement multiple EIIs static IMPL: u64 = 5; fn main() { diff --git a/tests/ui/eii/static/multiple_impls.stderr b/tests/ui/eii/static/multiple_impls.stderr new file mode 100644 index 0000000000000..b31331f2483f1 --- /dev/null +++ b/tests/ui/eii/static/multiple_impls.stderr @@ -0,0 +1,10 @@ +error: static cannot implement multiple EIIs + --> $DIR/multiple_impls.rs:14:1 + | +LL | #[b] + | ^^^^ + | + = note: this is not allowed because multiple externally implementable statics that alias may be unintuitive + +error: aborting due to 1 previous error + From 63b5ab2994f580d4a1f20edcfd7c7528963b68a7 Mon Sep 17 00:00:00 2001 From: Shun Sakai Date: Sat, 11 Apr 2026 18:38:21 +0900 Subject: [PATCH 16/33] Stabilize feature `int_lowest_highest_one` --- library/core/src/num/int_macros.rs | 10 ++++------ library/core/src/num/nonzero.rs | 10 ++++------ library/core/src/num/uint_macros.rs | 10 ++++------ library/coretests/tests/lib.rs | 1 - 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 6a403542dd237..605b7b324ea53 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -215,14 +215,13 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(int_lowest_highest_one)] - /// #[doc = concat!("assert_eq!(0b0_", stringify!($SelfT), ".highest_one(), None);")] #[doc = concat!("assert_eq!(0b1_", stringify!($SelfT), ".highest_one(), Some(0));")] #[doc = concat!("assert_eq!(0b1_0000_", stringify!($SelfT), ".highest_one(), Some(4));")] #[doc = concat!("assert_eq!(0b1_1111_", stringify!($SelfT), ".highest_one(), Some(4));")] /// ``` - #[unstable(feature = "int_lowest_highest_one", issue = "145203")] + #[stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -236,14 +235,13 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(int_lowest_highest_one)] - /// #[doc = concat!("assert_eq!(0b0_", stringify!($SelfT), ".lowest_one(), None);")] #[doc = concat!("assert_eq!(0b1_", stringify!($SelfT), ".lowest_one(), Some(0));")] #[doc = concat!("assert_eq!(0b1_0000_", stringify!($SelfT), ".lowest_one(), Some(4));")] #[doc = concat!("assert_eq!(0b1_1111_", stringify!($SelfT), ".lowest_one(), Some(0));")] /// ``` - #[unstable(feature = "int_lowest_highest_one", issue = "145203")] + #[stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index c270b947d4fd3..0d20275a7451b 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -713,8 +713,6 @@ macro_rules! nonzero_integer { /// # Examples /// /// ``` - /// #![feature(int_lowest_highest_one)] - /// /// # use core::num::NonZero; /// # fn main() { test().unwrap(); } /// # fn test() -> Option<()> { @@ -724,7 +722,8 @@ macro_rules! nonzero_integer { /// # Some(()) /// # } /// ``` - #[unstable(feature = "int_lowest_highest_one", issue = "145203")] + #[stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -737,8 +736,6 @@ macro_rules! nonzero_integer { /// # Examples /// /// ``` - /// #![feature(int_lowest_highest_one)] - /// /// # use core::num::NonZero; /// # fn main() { test().unwrap(); } /// # fn test() -> Option<()> { @@ -748,7 +745,8 @@ macro_rules! nonzero_integer { /// # Some(()) /// # } /// ``` - #[unstable(feature = "int_lowest_highest_one", issue = "145203")] + #[stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index c35deee920e46..3a3af7ac1f6ef 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -295,14 +295,13 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(int_lowest_highest_one)] - /// #[doc = concat!("assert_eq!(0b0_", stringify!($SelfT), ".highest_one(), None);")] #[doc = concat!("assert_eq!(0b1_", stringify!($SelfT), ".highest_one(), Some(0));")] #[doc = concat!("assert_eq!(0b1_0000_", stringify!($SelfT), ".highest_one(), Some(4));")] #[doc = concat!("assert_eq!(0b1_1111_", stringify!($SelfT), ".highest_one(), Some(4));")] /// ``` - #[unstable(feature = "int_lowest_highest_one", issue = "145203")] + #[stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -319,14 +318,13 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(int_lowest_highest_one)] - /// #[doc = concat!("assert_eq!(0b0_", stringify!($SelfT), ".lowest_one(), None);")] #[doc = concat!("assert_eq!(0b1_", stringify!($SelfT), ".lowest_one(), Some(0));")] #[doc = concat!("assert_eq!(0b1_0000_", stringify!($SelfT), ".lowest_one(), Some(4));")] #[doc = concat!("assert_eq!(0b1_1111_", stringify!($SelfT), ".lowest_one(), Some(0));")] /// ``` - #[unstable(feature = "int_lowest_highest_one", issue = "145203")] + #[stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "int_lowest_highest_one", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 129d2c013cd27..cecdb265780a4 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -66,7 +66,6 @@ #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(int_from_ascii)] -#![feature(int_lowest_highest_one)] #![feature(int_roundings)] #![feature(ip)] #![feature(is_ascii_octdigit)] From 21925e9e2353a24e9a2fc9dd42a1e4c681eacdf9 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:24:04 +0200 Subject: [PATCH 17/33] Lazily check diagnostic namespace features --- .../src/attributes/diagnostic/on_const.rs | 1 + .../src/attributes/diagnostic/on_unknown.rs | 1 + compiler/rustc_resolve/src/macros.rs | 25 +++++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs index def4069f6b477..23db854252a37 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs @@ -17,6 +17,7 @@ impl AttributeParser for OnConstParser { template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), |this, cx, args| { if !cx.features().diagnostic_on_const() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs return; } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs index bd5eb4cbf82c7..dcfba68a4cf8b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs @@ -18,6 +18,7 @@ impl OnUnknownParser { mode: Mode, ) { if !cx.features().diagnostic_on_unknown() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs return; } let span = cx.attr_span; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 67a896bdd7557..15244b7afb69f 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -712,25 +712,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit(); } - let diagnostic_attributes: &[(Symbol, bool)] = &[ - (sym::on_unimplemented, true), - (sym::do_not_recommend, true), - (sym::on_move, true), - (sym::on_const, self.tcx.features().diagnostic_on_const()), - (sym::on_unknown, self.tcx.features().diagnostic_on_unknown()), + const DIAGNOSTIC_ATTRIBUTES: &[(Symbol, Option)] = &[ + (sym::on_unimplemented, None), + (sym::do_not_recommend, None), + (sym::on_move, None), + (sym::on_const, Some(sym::diagnostic_on_const)), + (sym::on_unknown, Some(sym::diagnostic_on_unknown)), ]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic - && !diagnostic_attributes - .iter() - .any(|(attr, stable)| *stable && attribute.ident.name == *attr) + && !DIAGNOSTIC_ATTRIBUTES.iter().any(|(attr, stable)| { + attribute.ident.name == *attr + && stable.is_none_or(|f| self.tcx.features().enabled(f)) + }) { let span = attribute.span(); - let candidates = diagnostic_attributes + let candidates = DIAGNOSTIC_ATTRIBUTES .iter() - .filter_map(|(sym, stable)| stable.then_some(*sym)) + .filter_map(|(sym, stable)| { + stable.is_none_or(|f| self.tcx.features().enabled(f)).then_some(*sym) + }) .collect::>(); let typo = find_best_match_for_name(&candidates, attribute.ident.name, Some(5)) .map(|typo_name| errors::UnknownDiagnosticAttributeTypoSugg { span, typo_name }); From d69403abfc978657586e9ff85b71b988248b915e Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:42:04 +0200 Subject: [PATCH 18/33] Properly emit diagnostic for diagnostic::on_move being used without feature gate --- .../src/attributes/diagnostic/on_move.rs | 1 + compiler/rustc_resolve/src/macros.rs | 2 +- .../feature-gate-diagnostic-on-move.rs | 5 ++--- .../feature-gate-diagnostic-on-move.stderr | 16 ++++++++++++---- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs index 006b3b66658e0..a79b7d6afbcdc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs @@ -24,6 +24,7 @@ impl OnMoveParser { mode: Mode, ) { if !cx.features().diagnostic_on_move() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs return; } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 15244b7afb69f..7769b6d815454 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -715,7 +715,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { const DIAGNOSTIC_ATTRIBUTES: &[(Symbol, Option)] = &[ (sym::on_unimplemented, None), (sym::do_not_recommend, None), - (sym::on_move, None), + (sym::on_move, Some(sym::diagnostic_on_move)), (sym::on_const, Some(sym::diagnostic_on_const)), (sym::on_unknown, Some(sym::diagnostic_on_unknown)), ]; diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs index a1f3b1fbbc860..a55a6260d6512 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs @@ -2,9 +2,8 @@ //! gate, but the fact that not adding the feature gate will cause the //! diagnostic to not emit the custom diagnostic message //! -#[diagnostic::on_move( - message = "Foo" -)] +#[diagnostic::on_move(message = "Foo")] +//~^ WARN unknown diagnostic attribute #[derive(Debug)] struct Foo; diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr index 9ba6f272cf92b..83d6448ed5704 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr @@ -1,5 +1,13 @@ +warning: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-on-move.rs:5:15 + | +LL | #[diagnostic::on_move(message = "Foo")] + | ^^^^^^^ + | + = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + error[E0382]: use of moved value: `foo` - --> $DIR/feature-gate-diagnostic-on-move.rs:16:15 + --> $DIR/feature-gate-diagnostic-on-move.rs:15:15 | LL | let foo = Foo; | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait @@ -9,14 +17,14 @@ LL | let bar = foo; | ^^^ value used here after move | note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary - --> $DIR/feature-gate-diagnostic-on-move.rs:11:17 + --> $DIR/feature-gate-diagnostic-on-move.rs:10:17 | LL | fn takes_foo(_: Foo) {} | --------- ^^^ this parameter takes ownership of the value | | | in this function note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/feature-gate-diagnostic-on-move.rs:9:1 + --> $DIR/feature-gate-diagnostic-on-move.rs:8:1 | LL | struct Foo; | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -24,6 +32,6 @@ LL | struct Foo; LL | takes_foo(foo); | --- you could clone this value -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0382`. From 55cd47658c8ae2e09011ccbfee773bd03b704263 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:47:07 +0200 Subject: [PATCH 19/33] Change edit distance to not suggest `on_move` and `on_const` against each other --- compiler/rustc_resolve/src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 7769b6d815454..f106d88a83206 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -735,7 +735,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { stable.is_none_or(|f| self.tcx.features().enabled(f)).then_some(*sym) }) .collect::>(); - let typo = find_best_match_for_name(&candidates, attribute.ident.name, Some(5)) + let typo = find_best_match_for_name(&candidates, attribute.ident.name, None) .map(|typo_name| errors::UnknownDiagnosticAttributeTypoSugg { span, typo_name }); self.tcx.sess.psess.buffer_lint( From 106b16cd2d9e64b950924be6469fdc0854eb3515 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 12 Apr 2026 11:06:40 +0200 Subject: [PATCH 20/33] Add support for static EIIs in late resolution --- compiler/rustc_resolve/src/late.rs | 15 ++++++++++----- .../ui/eii/eii-declaration-not-fn-issue-152337.rs | 4 ++-- .../eii-declaration-not-fn-issue-152337.stderr | 8 ++++---- tests/ui/eii/static/multiple_decls.rs | 1 + tests/ui/eii/static/multiple_decls.stderr | 11 +++++++++-- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 540e7ec52f51c..56e26b1ac6cbe 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -530,9 +530,8 @@ impl PathSource<'_, '_, '_> { }, _ => "value", }, - PathSource::ReturnTypeNotation - | PathSource::Delegation - | PathSource::ExternItemImpl => "function", + PathSource::ReturnTypeNotation | PathSource::Delegation => "function", + PathSource::ExternItemImpl => "function or static", PathSource::PreciseCapturingArg(..) => "type or const parameter", PathSource::Macro => "macro", PathSource::Module => "module", @@ -625,7 +624,13 @@ impl PathSource<'_, '_, '_> { }, PathSource::Delegation => matches!(res, Res::Def(DefKind::Fn | DefKind::AssocFn, _)), PathSource::ExternItemImpl => { - matches!(res, Res::Def(DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..), _)) + matches!( + res, + Res::Def( + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::Static { .. }, + _ + ) + ) } PathSource::PreciseCapturingArg(ValueNS) => { matches!(res, Res::Def(DefKind::ConstParam, _)) @@ -5500,7 +5505,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { *node_id, &None, &target.foreign_item, - PathSource::ExternItemImpl + PathSource::ExternItemImpl, ); } else { self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); diff --git a/tests/ui/eii/eii-declaration-not-fn-issue-152337.rs b/tests/ui/eii/eii-declaration-not-fn-issue-152337.rs index 8564a5a748477..0d71f4854d344 100644 --- a/tests/ui/eii/eii-declaration-not-fn-issue-152337.rs +++ b/tests/ui/eii/eii-declaration-not-fn-issue-152337.rs @@ -6,7 +6,7 @@ const A: () = (); #[eii] fn A() {} //~ ERROR the name `A` is defined multiple times -//~^ ERROR expected function, found constant -//~| ERROR expected function, found constant +//~^ ERROR expected function or static, found constant +//~| ERROR expected function or static, found constant fn main() {} diff --git a/tests/ui/eii/eii-declaration-not-fn-issue-152337.stderr b/tests/ui/eii/eii-declaration-not-fn-issue-152337.stderr index ea4ec604e7aa4..34998f33cc92e 100644 --- a/tests/ui/eii/eii-declaration-not-fn-issue-152337.stderr +++ b/tests/ui/eii/eii-declaration-not-fn-issue-152337.stderr @@ -9,17 +9,17 @@ LL | fn A() {} | = note: `A` must be defined only once in the value namespace of this module -error[E0423]: expected function, found constant `self::A` +error[E0423]: expected function or static, found constant `self::A` --> $DIR/eii-declaration-not-fn-issue-152337.rs:8:4 | LL | fn A() {} - | ^ not a function + | ^ not a function or static -error[E0423]: expected function, found constant `A` +error[E0423]: expected function or static, found constant `A` --> $DIR/eii-declaration-not-fn-issue-152337.rs:8:4 | LL | fn A() {} - | ^ not a function + | ^ not a function or static error: aborting due to 3 previous errors diff --git a/tests/ui/eii/static/multiple_decls.rs b/tests/ui/eii/static/multiple_decls.rs index 1913dc39e8b7c..791e2725087f1 100644 --- a/tests/ui/eii/static/multiple_decls.rs +++ b/tests/ui/eii/static/multiple_decls.rs @@ -4,6 +4,7 @@ const A: () = (); #[eii(A)] static A: u64; //~^ ERROR the name `A` is defined multiple times +//~| ERROR expected function or static, found constant `A` #[A] static A_IMPL: u64 = 5; diff --git a/tests/ui/eii/static/multiple_decls.stderr b/tests/ui/eii/static/multiple_decls.stderr index b0b5da1aabe1b..fcc5d93c5f993 100644 --- a/tests/ui/eii/static/multiple_decls.stderr +++ b/tests/ui/eii/static/multiple_decls.stderr @@ -9,6 +9,13 @@ LL | static A: u64; | = note: `A` must be defined only once in the value namespace of this module -error: aborting due to 1 previous error +error[E0423]: expected function or static, found constant `A` + --> $DIR/multiple_decls.rs:5:8 + | +LL | static A: u64; + | ^ not a function or static + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0428`. +Some errors have detailed explanations: E0423, E0428. +For more information about an error, try `rustc --explain E0423`. From aa1a5f8a93df2afce47eb91a52f40bf0b5f0b524 Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 19 Feb 2026 02:04:18 +0530 Subject: [PATCH 21/33] Codegen non-overloaded LLVM intrinsics using their name --- compiler/rustc_codegen_llvm/src/errors.rs | 12 +++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 77 +++++++++++++++---- .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 1 - compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 + compiler/rustc_codegen_llvm/src/llvm/mod.rs | 4 + compiler/rustc_codegen_llvm/src/type_.rs | 8 ++ .../incorrect-llvm-intrinsic-signature.rs | 14 ++++ .../incorrect-llvm-intrinsic-signature.stderr | 8 ++ 8 files changed, 109 insertions(+), 18 deletions(-) create mode 100644 tests/ui/codegen/incorrect-llvm-intrinsic-signature.rs create mode 100644 tests/ui/codegen/incorrect-llvm-intrinsic-signature.stderr diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index caec20db4c2db..b679eccfc687e 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -211,3 +211,15 @@ pub(crate) struct FixedX18InvalidArch<'a> { "enabling both `-Zpacked-stack` and the `backchain` target feature is incompatible with the default s390x ABI. Switch to s390x-unknown-none-softfloat if you need both attributes" )] pub(crate) struct PackedStackBackchainNeedsSoftfloat; + +#[derive(Diagnostic)] +#[diag( + "intrinsic signature mismatch for `{$name}`: expected signature `{$llvm_fn_ty}`, found `{$rust_fn_ty}`" +)] +pub(crate) struct IntrinsicSignatureMismatch<'a> { + pub name: &'a str, + pub llvm_fn_ty: &'a str, + pub rust_fn_ty: &'a str, + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 3e600914d6f42..b0550f4786693 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; use std::ffi::c_uint; -use std::{assert_matches, ptr}; +use std::{assert_matches, iter, ptr}; use rustc_abi::{ Align, BackendRepr, ExternAbi, Float, HasDataLayout, NumScalableVectors, Primitive, Size, @@ -36,7 +36,8 @@ use crate::builder::gpu_offload::{ use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ - AutoDiffWithoutEnable, AutoDiffWithoutLto, OffloadWithoutEnable, OffloadWithoutFatLTO, + AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, OffloadWithoutEnable, + OffloadWithoutFatLTO, }; use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; @@ -847,35 +848,22 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { llargument_tys.push(arg_layout.immediate_llvm_type(self)); } - let fn_ty = self.type_func(&llargument_tys, llreturn_ty); - let fn_ptr = if let Some(&llfn) = self.intrinsic_instances.borrow().get(&instance) { llfn } else { let sym = tcx.symbol_name(instance).name; - // FIXME use get_intrinsic let llfn = if let Some(llfn) = self.get_declared_value(sym) { llfn } else { - // Function addresses in Rust are never significant, allowing functions to - // be merged. - let llfn = declare_raw_fn( - self, - sym, - llvm::CCallConv, - llvm::UnnamedAddr::Global, - llvm::Visibility::Default, - fn_ty, - ); - - llfn + intrinsic_fn(self, sym, llreturn_ty, llargument_tys, instance) }; self.intrinsic_instances.borrow_mut().insert(instance, llfn); llfn }; + let fn_ty = self.get_type_of_global(fn_ptr); let mut llargs = vec![]; @@ -976,6 +964,61 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } +fn intrinsic_fn<'ll, 'tcx>( + bx: &Builder<'_, 'll, 'tcx>, + name: &str, + rust_return_ty: &'ll Type, + rust_argument_tys: Vec<&'ll Type>, + instance: ty::Instance<'tcx>, +) -> &'ll Value { + let tcx = bx.tcx; + + let rust_fn_ty = bx.type_func(&rust_argument_tys, rust_return_ty); + + let intrinsic = llvm::Intrinsic::lookup(name.as_bytes()); + + if let Some(intrinsic) = intrinsic + && !intrinsic.is_overloaded() + { + // FIXME: also do this for overloaded intrinsics + let llfn = intrinsic.get_declaration(bx.llmod, &[]); + let llvm_fn_ty = bx.get_type_of_global(llfn); + + let llvm_return_ty = bx.get_return_type(llvm_fn_ty); + let llvm_argument_tys = bx.func_params_types(llvm_fn_ty); + let llvm_is_variadic = bx.func_is_variadic(llvm_fn_ty); + + let is_correct_signature = !llvm_is_variadic + && rust_argument_tys.len() == llvm_argument_tys.len() + && iter::once((rust_return_ty, llvm_return_ty)) + .chain(iter::zip(rust_argument_tys, llvm_argument_tys)) + .all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty); + + if !is_correct_signature { + tcx.dcx().emit_fatal(IntrinsicSignatureMismatch { + name, + llvm_fn_ty: &format!("{llvm_fn_ty:?}"), + rust_fn_ty: &format!("{rust_fn_ty:?}"), + span: tcx.def_span(instance.def_id()), + }); + } + + return llfn; + } + + // Function addresses in Rust are never significant, allowing functions to be merged. + let llfn = declare_raw_fn( + bx, + name, + llvm::CCallConv, + llvm::UnnamedAddr::Global, + llvm::Visibility::Default, + rust_fn_ty, + ); + + llfn +} + fn catch_unwind_intrinsic<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 67fbc0f53adc9..195e050a9b651 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -73,7 +73,6 @@ unsafe extern "C" { pub(crate) fn LLVMDumpModule(M: &Module); pub(crate) fn LLVMDumpValue(V: &Value); pub(crate) fn LLVMGetFunctionCallConv(F: &Value) -> c_uint; - pub(crate) fn LLVMGetReturnType(T: &Type) -> &Type; pub(crate) fn LLVMGetParams(Fnc: &Value, params: *mut &Value); pub(crate) fn LLVMGetNamedFunction(M: &Module, Name: *const c_char) -> Option<&Value>; } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index bc24f1692fcf2..7855afeced47d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -930,6 +930,8 @@ unsafe extern "C" { ) -> &'a Type; pub(crate) fn LLVMCountParamTypes(FunctionTy: &Type) -> c_uint; pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type); + pub(crate) fn LLVMGetReturnType(FunctionTy: &Type) -> &Type; + pub(crate) fn LLVMIsFunctionVarArg(FunctionTy: &Type) -> Bool; // Operations on struct types pub(crate) fn LLVMStructTypeInContext<'a>( @@ -1084,6 +1086,7 @@ unsafe extern "C" { // Operations about llvm intrinsics pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint; + pub(crate) fn LLVMIntrinsicIsOverloaded(ID: NonZero) -> Bool; pub(crate) fn LLVMGetIntrinsicDeclaration<'a>( Mod: &'a Module, ID: NonZero, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2871326b28b5a..84d7e8165fe03 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -323,6 +323,10 @@ impl Intrinsic { NonZero::new(id).map(|id| Self { id }) } + pub(crate) fn is_overloaded(self) -> bool { + unsafe { LLVMIntrinsicIsOverloaded(self.id).is_true() } + } + pub(crate) fn get_declaration<'ll>( self, llmod: &'ll Module, diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 2026b06d104df..b8cee3510789c 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -77,6 +77,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { unsafe { llvm::LLVMAddFunction(self.llmod(), name.as_ptr(), ty) } } + pub(crate) fn get_return_type(&self, ty: &'ll Type) -> &'ll Type { + unsafe { llvm::LLVMGetReturnType(ty) } + } + pub(crate) fn func_params_types(&self, ty: &'ll Type) -> Vec<&'ll Type> { unsafe { let n_args = llvm::LLVMCountParamTypes(ty) as usize; @@ -86,6 +90,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { args } } + + pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool { + unsafe { llvm::LLVMIsFunctionVarArg(ty).is_true() } + } } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn type_bool(&self) -> &'ll Type { diff --git a/tests/ui/codegen/incorrect-llvm-intrinsic-signature.rs b/tests/ui/codegen/incorrect-llvm-intrinsic-signature.rs new file mode 100644 index 0000000000000..4b86f37f922a6 --- /dev/null +++ b/tests/ui/codegen/incorrect-llvm-intrinsic-signature.rs @@ -0,0 +1,14 @@ +//@ build-fail +//@ ignore-backends: gcc + +#![feature(link_llvm_intrinsics, abi_unadjusted)] + +extern "unadjusted" { + #[link_name = "llvm.assume"] + fn foo(); + //~^ ERROR: intrinsic signature mismatch for `llvm.assume`: expected signature `void (i1)`, found `void ()` +} + +pub fn main() { + unsafe { foo() } +} diff --git a/tests/ui/codegen/incorrect-llvm-intrinsic-signature.stderr b/tests/ui/codegen/incorrect-llvm-intrinsic-signature.stderr new file mode 100644 index 0000000000000..4e58e5ebdc731 --- /dev/null +++ b/tests/ui/codegen/incorrect-llvm-intrinsic-signature.stderr @@ -0,0 +1,8 @@ +error: intrinsic signature mismatch for `llvm.assume`: expected signature `void (i1)`, found `void ()` + --> $DIR/incorrect-llvm-intrinsic-signature.rs:8:5 + | +LL | fn foo(); + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From 6733ce938fa5e74730462c0956a4cfabb7d085bc Mon Sep 17 00:00:00 2001 From: Ayuse Date: Sun, 12 Apr 2026 16:17:44 +0100 Subject: [PATCH 22/33] Add --verbose-run-make-subprocess-output flag to suppress run-make output Add a flag to control verbose subprocess output for run-make tests. When using --no-capture on panic=abort test suites like cg_clif, passing test output can flood the terminal. This flag (default true) lets users opt out via --verbose-run-make-subprocess-output=false. Extract a private print_command_output helper in run-make-support to avoid inlining the output logic in handle_failed_output, ensuring failures always print regardless of the flag. --- src/bootstrap/src/core/build_steps/test.rs | 4 ++++ src/bootstrap/src/core/config/flags.rs | 13 +++++++++++++ src/bootstrap/src/utils/change_tracker.rs | 5 +++++ src/doc/rustc-dev-guide/src/tests/compiletest.md | 9 +++++++++ src/etc/completions/x.fish | 2 ++ src/etc/completions/x.ps1 | 2 ++ src/etc/completions/x.py.fish | 2 ++ src/etc/completions/x.py.ps1 | 2 ++ src/etc/completions/x.py.sh | 12 ++++++++++-- src/etc/completions/x.py.zsh | 2 ++ src/etc/completions/x.sh | 12 ++++++++++-- src/etc/completions/x.zsh | 2 ++ src/tools/compiletest/src/common.rs | 4 ++++ src/tools/compiletest/src/lib.rs | 7 +++++++ src/tools/compiletest/src/runtest/run_make.rs | 7 +++++++ src/tools/compiletest/src/rustdoc_gui_test.rs | 1 + src/tools/run-make-support/src/util.rs | 13 +++++++++++-- 17 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c222aa2305647..991592ec522d0 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2334,6 +2334,10 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--verbose"); } + if builder.config.cmd.verbose_run_make_subprocess_output() { + cmd.arg("--verbose-run-make-subprocess-output"); + } + if builder.config.rustc_debug_assertions { cmd.arg("--with-rustc-debug-assertions"); } diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 2f1e234d6ebc0..e1b8aa9810c3d 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -422,6 +422,10 @@ pub enum Subcommand { #[arg(long)] /// don't capture stdout/stderr of tests no_capture: bool, + #[arg(long, default_value_t = true, action = clap::ArgAction::Set, default_missing_value = "true", num_args = 0..=1, require_equals = true)] + /// whether to show verbose subprocess output for run-make tests; + /// set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture) + verbose_run_make_subprocess_output: bool, #[arg(long)] /// Use a different codegen backend when running tests. test_codegen_backend: Option, @@ -631,6 +635,15 @@ impl Subcommand { } } + pub fn verbose_run_make_subprocess_output(&self) -> bool { + match *self { + Subcommand::Test { verbose_run_make_subprocess_output, .. } => { + verbose_run_make_subprocess_output + } + _ => true, + } + } + pub fn rustfix_coverage(&self) -> bool { match *self { Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage, diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 3ae2373e1da21..331403f959b4c 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -621,4 +621,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "`x.py` stopped accepting partial argument names. Use full names to avoid errors.", }, + ChangeInfo { + change_id: 154587, + severity: ChangeSeverity::Info, + summary: "New `--verbose-run-make-subprocess-output` flag for `x.py test` (defaults to true). Set `--verbose-run-make-subprocess-output=false` to suppress verbose subprocess output for passing run-make tests when using `--no-capture`.", + }, ]; diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 83fcfecc1de7b..e19c7a8d4e44f 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -460,6 +460,15 @@ However, revisions or building auxiliary via directives are not currently suppor `rmake.rs` and `run-make-support` may *not* use any nightly/unstable features, as they must be compilable by a stage 0 rustc that may be a beta or even stable rustc. +By default, run-make tests print each subprocess command and its stdout/stderr. +When running with `--no-capture` on `panic=abort` test suites (such as `cg_clif`), +this can flood the terminal. Omit `--verbose-run-make-subprocess-output` to +suppress this output for passing tests — failing tests always print regardless: + +```bash +./x test tests/run-make --no-capture --verbose-run-make-subprocess-output=false +``` + #### Quickly check if `rmake.rs` tests can be compiled You can quickly check if `rmake.rs` tests can be compiled without having to diff --git a/src/etc/completions/x.fish b/src/etc/completions/x.fish index 92af4f04dcba9..689a13452e1bc 100644 --- a/src/etc/completions/x.fish +++ b/src/etc/completions/x.fish @@ -420,6 +420,7 @@ complete -c x -n "__fish_x_using_subcommand test" -l extra-checks -d 'comma-sepa complete -c x -n "__fish_x_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x -n "__fish_x_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x -n "__fish_x_using_subcommand test" -l run -d 'whether to execute run-* tests' -r +complete -c x -n "__fish_x_using_subcommand test" -l verbose-run-make-subprocess-output -d 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)' -r -f -a "{true\t'',false\t''}" complete -c x -n "__fish_x_using_subcommand test" -l test-codegen-backend -d 'Use a different codegen backend when running tests' -r complete -c x -n "__fish_x_using_subcommand test" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand test" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" @@ -473,6 +474,7 @@ complete -c x -n "__fish_x_using_subcommand t" -l extra-checks -d 'comma-separat complete -c x -n "__fish_x_using_subcommand t" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x -n "__fish_x_using_subcommand t" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x -n "__fish_x_using_subcommand t" -l run -d 'whether to execute run-* tests' -r +complete -c x -n "__fish_x_using_subcommand t" -l verbose-run-make-subprocess-output -d 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)' -r -f -a "{true\t'',false\t''}" complete -c x -n "__fish_x_using_subcommand t" -l test-codegen-backend -d 'Use a different codegen backend when running tests' -r complete -c x -n "__fish_x_using_subcommand t" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand t" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" diff --git a/src/etc/completions/x.ps1 b/src/etc/completions/x.ps1 index d4bf45f738365..e99ef27c2abca 100644 --- a/src/etc/completions/x.ps1 +++ b/src/etc/completions/x.ps1 @@ -487,6 +487,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') + [CompletionResult]::new('--verbose-run-make-subprocess-output', '--verbose-run-make-subprocess-output', [CompletionResultType]::ParameterName, 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)') [CompletionResult]::new('--test-codegen-backend', '--test-codegen-backend', [CompletionResultType]::ParameterName, 'Use a different codegen backend when running tests') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') @@ -547,6 +548,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') + [CompletionResult]::new('--verbose-run-make-subprocess-output', '--verbose-run-make-subprocess-output', [CompletionResultType]::ParameterName, 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)') [CompletionResult]::new('--test-codegen-backend', '--test-codegen-backend', [CompletionResultType]::ParameterName, 'Use a different codegen backend when running tests') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 0dbbe1ea43efd..a852df8a7753e 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -420,6 +420,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand test" -l extra-checks -d 'comm complete -c x.py -n "__fish_x.py_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l run -d 'whether to execute run-* tests' -r +complete -c x.py -n "__fish_x.py_using_subcommand test" -l verbose-run-make-subprocess-output -d 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)' -r -f -a "{true\t'',false\t''}" complete -c x.py -n "__fish_x.py_using_subcommand test" -l test-codegen-backend -d 'Use a different codegen backend when running tests' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand test" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" @@ -473,6 +474,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand t" -l extra-checks -d 'comma-s complete -c x.py -n "__fish_x.py_using_subcommand t" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x.py -n "__fish_x.py_using_subcommand t" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x.py -n "__fish_x.py_using_subcommand t" -l run -d 'whether to execute run-* tests' -r +complete -c x.py -n "__fish_x.py_using_subcommand t" -l verbose-run-make-subprocess-output -d 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)' -r -f -a "{true\t'',false\t''}" complete -c x.py -n "__fish_x.py_using_subcommand t" -l test-codegen-backend -d 'Use a different codegen backend when running tests' -r complete -c x.py -n "__fish_x.py_using_subcommand t" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand t" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index b4e3b8c580a49..665cb812f2df9 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -487,6 +487,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') + [CompletionResult]::new('--verbose-run-make-subprocess-output', '--verbose-run-make-subprocess-output', [CompletionResultType]::ParameterName, 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)') [CompletionResult]::new('--test-codegen-backend', '--test-codegen-backend', [CompletionResultType]::ParameterName, 'Use a different codegen backend when running tests') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') @@ -547,6 +548,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') + [CompletionResult]::new('--verbose-run-make-subprocess-output', '--verbose-run-make-subprocess-output', [CompletionResultType]::ParameterName, 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)') [CompletionResult]::new('--test-codegen-backend', '--test-codegen-backend', [CompletionResultType]::ParameterName, 'Use a different codegen backend when running tests') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 8a7aee2a091c7..5e6db9bcb5325 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -4638,7 +4638,7 @@ _x.py() { return 0 ;; x.py__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose-run-make-subprocess-output --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4668,6 +4668,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --verbose-run-make-subprocess-output) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --test-codegen-backend) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -4852,7 +4856,7 @@ _x.py() { return 0 ;; x.py__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose-run-make-subprocess-output --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4882,6 +4886,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --verbose-run-make-subprocess-output) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --test-codegen-backend) COMPREPLY=($(compgen -f "${cur}")) return 0 diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index e24da218a05d8..1f8701c297baa 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -488,6 +488,7 @@ _arguments "${_arguments_options[@]}" : \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ +'--verbose-run-make-subprocess-output=[whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)]' \ '--test-codegen-backend=[Use a different codegen backend when running tests]:TEST_CODEGEN_BACKEND:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ @@ -550,6 +551,7 @@ _arguments "${_arguments_options[@]}" : \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ +'--verbose-run-make-subprocess-output=[whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)]' \ '--test-codegen-backend=[Use a different codegen backend when running tests]:TEST_CODEGEN_BACKEND:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ diff --git a/src/etc/completions/x.sh b/src/etc/completions/x.sh index a4b44b73f47af..6314fe1307dcb 100644 --- a/src/etc/completions/x.sh +++ b/src/etc/completions/x.sh @@ -4638,7 +4638,7 @@ _x() { return 0 ;; x__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose-run-make-subprocess-output --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4668,6 +4668,10 @@ _x() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --verbose-run-make-subprocess-output) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --test-codegen-backend) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -4852,7 +4856,7 @@ _x() { return 0 ;; x__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose-run-make-subprocess-output --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4882,6 +4886,10 @@ _x() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --verbose-run-make-subprocess-output) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --test-codegen-backend) COMPREPLY=($(compgen -f "${cur}")) return 0 diff --git a/src/etc/completions/x.zsh b/src/etc/completions/x.zsh index 7f43781684b4b..12f441012e60f 100644 --- a/src/etc/completions/x.zsh +++ b/src/etc/completions/x.zsh @@ -488,6 +488,7 @@ _arguments "${_arguments_options[@]}" : \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ +'--verbose-run-make-subprocess-output=[whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)]' \ '--test-codegen-backend=[Use a different codegen backend when running tests]:TEST_CODEGEN_BACKEND:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ @@ -550,6 +551,7 @@ _arguments "${_arguments_options[@]}" : \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ +'--verbose-run-make-subprocess-output=[whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)]' \ '--test-codegen-backend=[Use a different codegen backend when running tests]:TEST_CODEGEN_BACKEND:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index c167580d99d9a..f73d7c96f7e85 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -603,6 +603,10 @@ pub(crate) struct Config { /// FIXME: this is *way* too coarse; the user can't select *which* info to verbosely dump. pub(crate) verbose: bool, + /// Whether to enable verbose subprocess output for run-make tests. + /// Set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture). + pub verbose_run_make_subprocess_output: bool, + /// Where to find the remote test client process, if we're using it. /// /// Note: this is *only* used for target platform executables created by `run-make` test diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index a23e3bb9a90e4..dcea79b18e6a8 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -134,6 +134,11 @@ fn parse_config(args: Vec) -> Config { ) .optflag("", "optimize-tests", "run tests with optimizations enabled") .optflag("", "verbose", "run tests verbosely, showing all output") + .optflag( + "", + "verbose-run-make-subprocess-output", + "show verbose subprocess output for successful run-make tests", + ) .optflag( "", "bless", @@ -471,6 +476,8 @@ fn parse_config(args: Vec) -> Config { adb_test_dir, adb_device_status, verbose: matches.opt_present("verbose"), + verbose_run_make_subprocess_output: matches + .opt_present("verbose-run-make-subprocess-output"), only_modified: matches.opt_present("only-modified"), remote_test_client: matches.opt_str("remote-test-client").map(Utf8PathBuf::from), compare_mode, diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index ac8846a263c0d..1044683ae6426 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -231,6 +231,13 @@ impl TestCx<'_> { } // Guard against externally-set env vars. + // Set env var to enable verbose output for successful commands. + // Only set when --verbose-run-make-subprocess-output is passed. + cmd.env_remove("__RMAKE_VERBOSE_SUBPROCESS_OUTPUT"); + if self.config.verbose_run_make_subprocess_output { + cmd.env("__RMAKE_VERBOSE_SUBPROCESS_OUTPUT", "1"); + } + cmd.env_remove("__RUSTC_DEBUG_ASSERTIONS_ENABLED"); if self.config.with_rustc_debug_assertions { // Used for `run_make_support::env::rustc_debug_assertions_enabled`. diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index c71fd714aa660..57ce0c5a6d5f7 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -109,6 +109,7 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { adb_test_dir: Default::default(), adb_device_status: Default::default(), verbose: Default::default(), + verbose_run_make_subprocess_output: Default::default(), remote_test_client: Default::default(), compare_mode: Default::default(), rustfix_coverage: Default::default(), diff --git a/src/tools/run-make-support/src/util.rs b/src/tools/run-make-support/src/util.rs index f44b3861d11d3..93ec44d58d791 100644 --- a/src/tools/run-make-support/src/util.rs +++ b/src/tools/run-make-support/src/util.rs @@ -4,7 +4,7 @@ use crate::command::{Command, CompletedProcess}; use crate::env::env_var; use crate::path_helpers::cwd; -pub(crate) fn verbose_print_command(cmd: &Command, output: &CompletedProcess) { +fn print_command_output(cmd: &Command, output: &CompletedProcess) { cmd.inspect(|std_cmd| { eprintln!("{std_cmd:?}"); }); @@ -16,6 +16,15 @@ pub(crate) fn verbose_print_command(cmd: &Command, output: &CompletedProcess) { } } +pub(crate) fn verbose_print_command(cmd: &Command, output: &CompletedProcess) { + // Only prints when `--verbose-run-make-subprocess-output` is active (env var set), + // so that passing tests don't flood the terminal when using `--no-capture`. + if std::env::var_os("__RMAKE_VERBOSE_SUBPROCESS_OUTPUT").is_none() { + return; + } + print_command_output(cmd, output); +} + /// If a given [`Command`] failed (as indicated by its [`CompletedProcess`]), verbose print the /// executed command, failure location, output status and stdout/stderr, and abort the process with /// exit code `1`. @@ -29,7 +38,7 @@ pub(crate) fn handle_failed_output( } else { eprintln!("command failed at line {caller_line_number}"); } - verbose_print_command(cmd, &output); + print_command_output(cmd, &output); std::process::exit(1) } From 08a12dc1432edd57898f3cb6b154a72977937fde Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 12 Apr 2026 18:38:17 +0200 Subject: [PATCH 23/33] Add feature hint for unstable diagnostic attributes --- compiler/rustc_resolve/src/errors.rs | 26 +++++++------ compiler/rustc_resolve/src/macros.rs | 38 ++++++++++++++----- .../feature-gate-diagnostic-on-move.stderr | 1 + .../feature-gate-diagnostic-on-unknown.stderr | 1 + 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index bcc754fd984da..8c7bf61949a29 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1521,20 +1521,24 @@ pub(crate) struct RedundantImportVisibility { #[diag("unknown diagnostic attribute")] pub(crate) struct UnknownDiagnosticAttribute { #[subdiagnostic] - pub typo: Option, + pub help: Option, } #[derive(Subdiagnostic)] -#[suggestion( - "an attribute with a similar name exists", - style = "verbose", - code = "{typo_name}", - applicability = "machine-applicable" -)] -pub(crate) struct UnknownDiagnosticAttributeTypoSugg { - #[primary_span] - pub span: Span, - pub typo_name: Symbol, +pub(crate) enum UnknownDiagnosticAttributeHelp { + #[suggestion( + "an attribute with a similar name exists", + style = "verbose", + code = "{typo_name}", + applicability = "machine-applicable" + )] + Typo { + #[primary_span] + span: Span, + typo_name: Symbol, + }, + #[help("add `#![feature({$feature})]` to the crate attributes to enable")] + UseFeature { feature: Symbol }, } // FIXME: Make this properly translatable. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index f106d88a83206..13bda9c98c0a7 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -723,26 +723,44 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic - && !DIAGNOSTIC_ATTRIBUTES.iter().any(|(attr, stable)| { + && !DIAGNOSTIC_ATTRIBUTES.iter().any(|(attr, feature)| { attribute.ident.name == *attr - && stable.is_none_or(|f| self.tcx.features().enabled(f)) + && feature.is_none_or(|f| self.tcx.features().enabled(f)) }) { + let name = attribute.ident.name; let span = attribute.span(); - let candidates = DIAGNOSTIC_ATTRIBUTES - .iter() - .filter_map(|(sym, stable)| { - stable.is_none_or(|f| self.tcx.features().enabled(f)).then_some(*sym) + + let help = 'help: { + if self.tcx.sess.is_nightly_build() { + for (attr, feature) in DIAGNOSTIC_ATTRIBUTES { + if let Some(feature) = *feature + && *attr == name + { + break 'help Some(errors::UnknownDiagnosticAttributeHelp::UseFeature { + feature, + }); + } + } + } + + let candidates = DIAGNOSTIC_ATTRIBUTES + .iter() + .filter_map(|(attr, feature)| { + feature.is_none_or(|f| self.tcx.features().enabled(f)).then_some(*attr) + }) + .collect::>(); + + find_best_match_for_name(&candidates, name, None).map(|typo_name| { + errors::UnknownDiagnosticAttributeHelp::Typo { span, typo_name } }) - .collect::>(); - let typo = find_best_match_for_name(&candidates, attribute.ident.name, None) - .map(|typo_name| errors::UnknownDiagnosticAttributeTypoSugg { span, typo_name }); + }; self.tcx.sess.psess.buffer_lint( UNKNOWN_DIAGNOSTIC_ATTRIBUTES, span, node_id, - errors::UnknownDiagnosticAttribute { typo }, + errors::UnknownDiagnosticAttribute { help }, ); } diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr index 83d6448ed5704..593120edd1700 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr @@ -4,6 +4,7 @@ warning: unknown diagnostic attribute LL | #[diagnostic::on_move(message = "Foo")] | ^^^^^^^ | + = help: add `#![feature(diagnostic_on_move)]` to the crate attributes to enable = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default error[E0382]: use of moved value: `foo` diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr index f6d7ffadaceae..d9c8071339b75 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr @@ -10,6 +10,7 @@ error: unknown diagnostic attribute LL | #[diagnostic::on_unknown(message = "Tada")] | ^^^^^^^^^^ | + = help: add `#![feature(diagnostic_on_unknown)]` to the crate attributes to enable note: the lint level is defined here --> $DIR/feature-gate-diagnostic-on-unknown.rs:1:9 | From c21f4ee437b28e923de735f4abc6fa1788bf1049 Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 13:13:47 +0530 Subject: [PATCH 24/33] Check for AutoUpgraded intrinsics, and lint on uses of deprecated intrinsics --- compiler/rustc_codegen_llvm/src/errors.rs | 8 ++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 34 +++++++++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 ++ compiler/rustc_lint_defs/src/builtin.rs | 46 +++++++++++++++++++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 10 ++++ tests/run-make/simd-ffi/simd.rs | 2 +- tests/ui/codegen/deprecated-llvm-intrinsic.rs | 28 +++++++++++ .../codegen/deprecated-llvm-intrinsic.stderr | 14 ++++++ tests/ui/codegen/unknown-llvm-intrinsic.rs | 14 ++++++ .../ui/codegen/unknown-llvm-intrinsic.stderr | 8 ++++ 10 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 tests/ui/codegen/deprecated-llvm-intrinsic.rs create mode 100644 tests/ui/codegen/deprecated-llvm-intrinsic.stderr create mode 100644 tests/ui/codegen/unknown-llvm-intrinsic.rs create mode 100644 tests/ui/codegen/unknown-llvm-intrinsic.stderr diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index b679eccfc687e..259aa20b9e381 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -223,3 +223,11 @@ pub(crate) struct IntrinsicSignatureMismatch<'a> { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag("unknown LLVM intrinsic `{$name}`")] +pub(crate) struct UnknownIntrinsic<'a> { + pub name: &'a str, + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index b0550f4786693..2655b45d6e940 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -21,6 +21,7 @@ use rustc_middle::ty::offload_meta::OffloadMetadata; use rustc_middle::ty::{self, GenericArgsRef, Instance, SimdAlign, Ty, TyCtxt, TypingEnv}; use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; +use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; @@ -37,7 +38,7 @@ use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, OffloadWithoutEnable, - OffloadWithoutFatLTO, + OffloadWithoutFatLTO, UnknownIntrinsic, }; use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; @@ -1016,6 +1017,37 @@ fn intrinsic_fn<'ll, 'tcx>( rust_fn_ty, ); + if intrinsic.is_none() { + let mut new_llfn = None; + let can_upgrade = unsafe { llvm::LLVMRustUpgradeIntrinsicFunction(llfn, &mut new_llfn) }; + + if !can_upgrade { + // This is either plain wrong, or this can be caused by incompatible LLVM versions + tcx.dcx().emit_fatal(UnknownIntrinsic { name, span: tcx.def_span(instance.def_id()) }); + } else if let Some(def_id) = instance.def_id().as_local() { + // we can emit diagnostics only for local crates + let hir_id = tcx.local_def_id_to_hir_id(def_id); + + // not all intrinsics are upgraded to some other intrinsics, most are upgraded to instruction sequences + let msg = if let Some(new_llfn) = new_llfn { + format!( + "using deprecated intrinsic `{name}`, `{}` can be used instead", + str::from_utf8(&llvm::get_value_name(new_llfn)).unwrap() + ) + } else { + format!("using deprecated intrinsic `{name}`") + }; + + tcx.emit_node_lint( + DEPRECATED_LLVM_INTRINSIC, + hir_id, + rustc_errors::DiagDecorator(|d| { + d.primary_message(msg).span(tcx.hir_span(hir_id)); + }), + ); + } + } + llfn } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7855afeced47d..3b81fde640596 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1093,6 +1093,10 @@ unsafe extern "C" { ParamTypes: *const &'a Type, ParamCount: size_t, ) -> &'a Value; + pub(crate) fn LLVMRustUpgradeIntrinsicFunction<'a>( + Fn: &'a Value, + NewFn: &mut Option<&'a Value>, + ) -> bool; // Operations on parameters pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4aff294aeac61..8af8f40d69f56 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -37,6 +37,7 @@ declare_lint_pass! { DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, DEPRECATED, DEPRECATED_IN_FUTURE, + DEPRECATED_LLVM_INTRINSIC, DEPRECATED_SAFE_2024, DEPRECATED_WHERE_CLAUSE_LOCATION, DUPLICATE_FEATURES, @@ -5597,3 +5598,48 @@ declare_lint! { report_in_deps: false, }; } + +declare_lint! { + /// The `deprecated_llvm_intrinsic` lint detects usage of deprecated LLVM intrinsics. + /// + /// ### Example + /// + /// ```rust,ignore (requires x86) + /// #![cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #![feature(link_llvm_intrinsics, abi_unadjusted)] + /// #![deny(deprecated_llvm_intrinsic)] + /// + /// unsafe extern "unadjusted" { + /// #[link_name = "llvm.x86.addcarryx.u32"] + /// fn foo(a: u8, b: u32, c: u32, d: &mut u32) -> u8; + /// } + /// + /// #[inline(never)] + /// #[target_feature(enable = "adx")] + /// pub fn bar(a: u8, b: u32, c: u32, d: &mut u32) -> u8 { + /// unsafe { foo(a, b, c, d) } + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: Using deprecated intrinsic `llvm.x86.addcarryx.u32` + /// --> example.rs:7:5 + /// | + /// 7 | fn foo(a: u8, b: u32, c: u32, d: &mut u32) -> u8; + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// | + /// ``` + /// + /// ### Explanation + /// + /// LLVM periodically updates its list of intrinsics. Deprecated intrinsics are unlikely + /// to be removed, but they may optimize less well than their new versions, so it's + /// best to use the new version. Also, some deprecated intrinsics might have buggy + /// behavior + pub DEPRECATED_LLVM_INTRINSIC, + Allow, + "detects uses of deprecated LLVM intrinsics", + @feature_gate = link_llvm_intrinsics; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index f7fccf6296bd1..30410a4d26fc4 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -9,6 +9,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DiagnosticHandler.h" @@ -1815,6 +1816,15 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) { GV.setSanitizerMetadata(MD); } +extern "C" bool LLVMRustUpgradeIntrinsicFunction(LLVMValueRef Fn, + LLVMValueRef *NewFn) { + Function *F = unwrap(Fn); + Function *NewF = nullptr; + bool CanUpgrade = UpgradeIntrinsicFunction(F, NewF, false); + *NewFn = wrap(NewF); + return CanUpgrade; +} + // Statically assert that the fixed metadata kind IDs declared in // `metadata_kind.rs` match the ones actually used by LLVM. #define FIXED_MD_KIND(VARIANT, VALUE) \ diff --git a/tests/run-make/simd-ffi/simd.rs b/tests/run-make/simd-ffi/simd.rs index 1cd961ff87e7b..3f12dabdb65ea 100644 --- a/tests/run-make/simd-ffi/simd.rs +++ b/tests/run-make/simd-ffi/simd.rs @@ -35,7 +35,7 @@ extern "C" { fn integer(a: i32x4, b: i32x4) -> i32x4; // vmaxq_s32 #[cfg(target_arch = "aarch64")] - #[link_name = "llvm.aarch64.neon.maxs.v4i32"] + #[link_name = "llvm.aarch64.neon.smax.v4i32"] fn integer(a: i32x4, b: i32x4) -> i32x4; // Use a generic LLVM intrinsic to do type checking on other platforms diff --git a/tests/ui/codegen/deprecated-llvm-intrinsic.rs b/tests/ui/codegen/deprecated-llvm-intrinsic.rs new file mode 100644 index 0000000000000..33bc5f419151a --- /dev/null +++ b/tests/ui/codegen/deprecated-llvm-intrinsic.rs @@ -0,0 +1,28 @@ +//@ add-minicore +//@ build-fail +//@ compile-flags: --target aarch64-unknown-linux-gnu +//@ needs-llvm-components: aarch64 +//@ ignore-backends: gcc +#![feature(no_core, lang_items, link_llvm_intrinsics, abi_unadjusted, repr_simd, simd_ffi)] +#![no_std] +#![no_core] +#![allow(internal_features, non_camel_case_types, improper_ctypes)] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[repr(simd)] +pub struct i8x8([i8; 8]); + +extern "unadjusted" { + #[deny(deprecated_llvm_intrinsic)] + #[link_name = "llvm.aarch64.neon.rbit.v8i8"] + fn foo(a: i8x8) -> i8x8; + //~^ ERROR: using deprecated intrinsic `llvm.aarch64.neon.rbit.v8i8`, `llvm.bitreverse.v8i8` can be used instead +} + +#[target_feature(enable = "neon")] +pub unsafe fn bar(a: i8x8) -> i8x8 { + foo(a) +} diff --git a/tests/ui/codegen/deprecated-llvm-intrinsic.stderr b/tests/ui/codegen/deprecated-llvm-intrinsic.stderr new file mode 100644 index 0000000000000..40e4684a8ea4f --- /dev/null +++ b/tests/ui/codegen/deprecated-llvm-intrinsic.stderr @@ -0,0 +1,14 @@ +error: using deprecated intrinsic `llvm.aarch64.neon.rbit.v8i8`, `llvm.bitreverse.v8i8` can be used instead + --> $DIR/deprecated-llvm-intrinsic.rs:21:5 + | +LL | fn foo(a: i8x8) -> i8x8; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deprecated-llvm-intrinsic.rs:19:12 + | +LL | #[deny(deprecated_llvm_intrinsic)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/codegen/unknown-llvm-intrinsic.rs b/tests/ui/codegen/unknown-llvm-intrinsic.rs new file mode 100644 index 0000000000000..bbb4df8c0b271 --- /dev/null +++ b/tests/ui/codegen/unknown-llvm-intrinsic.rs @@ -0,0 +1,14 @@ +//@ build-fail +//@ ignore-backends: gcc + +#![feature(link_llvm_intrinsics, abi_unadjusted)] + +extern "unadjusted" { + #[link_name = "llvm.abcde"] + fn foo(); + //~^ ERROR: unknown LLVM intrinsic `llvm.abcde` +} + +pub fn main() { + unsafe { foo() } +} diff --git a/tests/ui/codegen/unknown-llvm-intrinsic.stderr b/tests/ui/codegen/unknown-llvm-intrinsic.stderr new file mode 100644 index 0000000000000..5417140c69795 --- /dev/null +++ b/tests/ui/codegen/unknown-llvm-intrinsic.stderr @@ -0,0 +1,8 @@ +error: unknown LLVM intrinsic `llvm.abcde` + --> $DIR/unknown-llvm-intrinsic.rs:8:5 + | +LL | fn foo(); + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From a5372be2a15f7ec0c06855e209936e44bd42f482 Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 13:30:09 +0530 Subject: [PATCH 25/33] Add target arch verification for LLVM intrinsics --- compiler/rustc_codegen_llvm/src/errors.rs | 9 ++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 43 +++++++++++++++++-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_codegen_llvm/src/llvm/mod.rs | 4 ++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 4 ++ tests/ui/codegen/incorrect-arch-intrinsic.rs | 18 ++++++++ .../codegen/incorrect-arch-intrinsic.stderr | 8 ++++ 7 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 tests/ui/codegen/incorrect-arch-intrinsic.rs create mode 100644 tests/ui/codegen/incorrect-arch-intrinsic.stderr diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 259aa20b9e381..8921395ab76ce 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -231,3 +231,12 @@ pub(crate) struct UnknownIntrinsic<'a> { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag("intrinsic `{$name}` cannot be used with target arch `{$target_arch}`")] +pub(crate) struct IntrinsicWrongArch<'a> { + pub name: &'a str, + pub target_arch: &'a str, + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 2655b45d6e940..4c66c4ef8bdde 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -25,7 +25,7 @@ use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::Os; +use rustc_target::spec::{Arch, Os}; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -37,8 +37,8 @@ use crate::builder::gpu_offload::{ use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ - AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, OffloadWithoutEnable, - OffloadWithoutFatLTO, UnknownIntrinsic, + AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, IntrinsicWrongArch, + OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic, }; use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; @@ -965,6 +965,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } +fn llvm_arch_for(rust_arch: &Arch) -> Option<&'static str> { + Some(match rust_arch { + Arch::AArch64 | Arch::Arm64EC => "aarch64", + Arch::AmdGpu => "amdgcn", + Arch::Arm => "arm", + Arch::Bpf => "bpf", + Arch::Hexagon => "hexagon", + Arch::LoongArch32 | Arch::LoongArch64 => "loongarch", + Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => "mips", + Arch::Nvptx64 => "nvvm", + Arch::PowerPC | Arch::PowerPC64 => "ppc", + Arch::RiscV32 | Arch::RiscV64 => "riscv", + Arch::S390x => "s390", + Arch::SpirV => "spv", + Arch::Wasm32 | Arch::Wasm64 => "wasm", + Arch::X86 | Arch::X86_64 => "x86", + _ => return None, // fallback for unknown archs + }) +} + fn intrinsic_fn<'ll, 'tcx>( bx: &Builder<'_, 'll, 'tcx>, name: &str, @@ -978,6 +998,23 @@ fn intrinsic_fn<'ll, 'tcx>( let intrinsic = llvm::Intrinsic::lookup(name.as_bytes()); + if let Some(intrinsic) = intrinsic + && intrinsic.is_target_specific() + { + let (llvm_arch, _) = name[5..].split_once('.').unwrap(); + let rust_arch = &tcx.sess.target.arch; + + if let Some(correct_llvm_arch) = llvm_arch_for(rust_arch) + && llvm_arch != correct_llvm_arch + { + tcx.dcx().emit_fatal(IntrinsicWrongArch { + name, + target_arch: rust_arch.desc(), + span: tcx.def_span(instance.def_id()), + }); + } + } + if let Some(intrinsic) = intrinsic && !intrinsic.is_overloaded() { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 3b81fde640596..7edbaf5a5f33c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1097,6 +1097,7 @@ unsafe extern "C" { Fn: &'a Value, NewFn: &mut Option<&'a Value>, ) -> bool; + pub(crate) fn LLVMRustIsTargetIntrinsic(ID: NonZero) -> bool; // Operations on parameters pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 84d7e8165fe03..2ec19b1795b5a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -327,6 +327,10 @@ impl Intrinsic { unsafe { LLVMIntrinsicIsOverloaded(self.id).is_true() } } + pub(crate) fn is_target_specific(self) -> bool { + unsafe { LLVMRustIsTargetIntrinsic(self.id) } + } + pub(crate) fn get_declaration<'ll>( self, llmod: &'ll Module, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 30410a4d26fc4..c310e580af559 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1825,6 +1825,10 @@ extern "C" bool LLVMRustUpgradeIntrinsicFunction(LLVMValueRef Fn, return CanUpgrade; } +extern "C" bool LLVMRustIsTargetIntrinsic(unsigned ID) { + return Intrinsic::isTargetIntrinsic(ID); +} + // Statically assert that the fixed metadata kind IDs declared in // `metadata_kind.rs` match the ones actually used by LLVM. #define FIXED_MD_KIND(VARIANT, VALUE) \ diff --git a/tests/ui/codegen/incorrect-arch-intrinsic.rs b/tests/ui/codegen/incorrect-arch-intrinsic.rs new file mode 100644 index 0000000000000..9576cb8f81318 --- /dev/null +++ b/tests/ui/codegen/incorrect-arch-intrinsic.rs @@ -0,0 +1,18 @@ +//@ build-fail +//@ ignore-s390x +//@ normalize-stderr: "target arch `(.*)`" -> "target arch `TARGET_ARCH`" +//@ ignore-backends: gcc + +#![feature(link_llvm_intrinsics, abi_unadjusted)] + +extern "unadjusted" { + #[link_name = "llvm.s390.sfpc"] + fn foo(a: i32); + //~^ ERROR: intrinsic `llvm.s390.sfpc` cannot be used with target arch +} + +pub fn main() { + unsafe { + foo(0); + } +} diff --git a/tests/ui/codegen/incorrect-arch-intrinsic.stderr b/tests/ui/codegen/incorrect-arch-intrinsic.stderr new file mode 100644 index 0000000000000..5b44419aa741f --- /dev/null +++ b/tests/ui/codegen/incorrect-arch-intrinsic.stderr @@ -0,0 +1,8 @@ +error: intrinsic `llvm.s390.sfpc` cannot be used with target arch `TARGET_ARCH` + --> $DIR/incorrect-arch-intrinsic.rs:10:5 + | +LL | fn foo(a: i32); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 3d89a5be5099cbdf4a9dd3c40d198b573246c0ae Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 22:39:43 +0530 Subject: [PATCH 26/33] Add autocasts for structs --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 85 +++++++++++++++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 + compiler/rustc_codegen_llvm/src/type_.rs | 10 +++ tests/codegen-llvm/inject-autocast.rs | 39 +++++++++ 4 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 tests/codegen-llvm/inject-autocast.rs diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 4c66c4ef8bdde..6aedb6d97d0f8 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -820,7 +820,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Self::Value>], - is_cleanup: bool, + _is_cleanup: bool, ) -> Self::Value { let tcx = self.tcx(); @@ -871,7 +871,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { for arg in args { match arg.val { OperandValue::ZeroSized => {} - OperandValue::Immediate(_) => llargs.push(arg.immediate()), + OperandValue::Immediate(a) => llargs.push(a), OperandValue::Pair(a, b) => { llargs.push(a); llargs.push(b); @@ -897,24 +897,38 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } debug!("call intrinsic {:?} with args ({:?})", instance, llargs); - let args = self.check_call("call", fn_ty, fn_ptr, &llargs); + + for (dest_ty, arg) in iter::zip(self.func_params_types(fn_ty), &mut llargs) { + let src_ty = self.val_ty(arg); + assert!( + can_autocast(self, src_ty, dest_ty), + "Cannot match `{dest_ty:?}` (expected) with {src_ty:?} (found) in `{fn_ptr:?}" + ); + + *arg = autocast(self, arg, src_ty, dest_ty); + } + let llret = unsafe { llvm::LLVMBuildCallWithOperandBundles( self.llbuilder, fn_ty, fn_ptr, - args.as_ptr() as *const &llvm::Value, - args.len() as c_uint, + llargs.as_ptr(), + llargs.len() as c_uint, ptr::dangling(), 0, c"".as_ptr(), ) }; - if is_cleanup { - self.apply_attrs_to_cleanup_callsite(llret); - } - llret + let src_ty = self.val_ty(llret); + let dest_ty = llreturn_ty; + assert!( + can_autocast(self, dest_ty, src_ty), + "Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{fn_ptr:?}`" + ); + + autocast(self, llret, src_ty, dest_ty) } fn abort(&mut self) { @@ -985,6 +999,57 @@ fn llvm_arch_for(rust_arch: &Arch) -> Option<&'static str> { }) } +fn can_autocast<'ll>(cx: &CodegenCx<'ll, '_>, rust_ty: &'ll Type, llvm_ty: &'ll Type) -> bool { + if rust_ty == llvm_ty { + return true; + } + + // Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust + // due to auto field-alignment in non-packed structs (packed structs are represented in LLVM + // as, well, packed structs, so they won't match with those either) + if cx.type_kind(llvm_ty) == TypeKind::Struct && cx.type_kind(rust_ty) == TypeKind::Struct { + let rust_element_tys = cx.struct_element_types(rust_ty); + let llvm_element_tys = cx.struct_element_types(llvm_ty); + + if rust_element_tys.len() != llvm_element_tys.len() { + return false; + } + + iter::zip(rust_element_tys, llvm_element_tys).all(|(rust_element_ty, llvm_element_ty)| { + can_autocast(cx, rust_element_ty, llvm_element_ty) + }) + } else { + false + } +} + +fn autocast<'ll>( + bx: &mut Builder<'_, 'll, '_>, + val: &'ll Value, + src_ty: &'ll Type, + dest_ty: &'ll Type, +) -> &'ll Value { + if src_ty == dest_ty { + return val; + } + match (bx.type_kind(src_ty), bx.type_kind(dest_ty)) { + // re-pack structs + (TypeKind::Struct, TypeKind::Struct) => { + let mut ret = bx.const_poison(dest_ty); + for (idx, (src_element_ty, dest_element_ty)) in + iter::zip(bx.struct_element_types(src_ty), bx.struct_element_types(dest_ty)) + .enumerate() + { + let elt = bx.extract_value(val, idx as u64); + let casted_elt = autocast(bx, elt, src_element_ty, dest_element_ty); + ret = bx.insert_value(ret, casted_elt, idx as u64); + } + ret + } + _ => unreachable!(), + } +} + fn intrinsic_fn<'ll, 'tcx>( bx: &Builder<'_, 'll, 'tcx>, name: &str, @@ -1030,7 +1095,7 @@ fn intrinsic_fn<'ll, 'tcx>( && rust_argument_tys.len() == llvm_argument_tys.len() && iter::once((rust_return_ty, llvm_return_ty)) .chain(iter::zip(rust_argument_tys, llvm_argument_tys)) - .all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty); + .all(|(rust_ty, llvm_ty)| can_autocast(bx, rust_ty, llvm_ty)); if !is_correct_signature { tcx.dcx().emit_fatal(IntrinsicSignatureMismatch { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7edbaf5a5f33c..deafa38b7be64 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1613,6 +1613,9 @@ unsafe extern "C" { Packed: Bool, ); + pub(crate) fn LLVMCountStructElementTypes(StructTy: &Type) -> c_uint; + pub(crate) fn LLVMGetStructElementTypes<'a>(StructTy: &'a Type, Dest: *mut &'a Type); + pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; pub(crate) safe fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index b8cee3510789c..147056a5885a6 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -94,6 +94,16 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool { unsafe { llvm::LLVMIsFunctionVarArg(ty).is_true() } } + + pub(crate) fn struct_element_types(&self, ty: &'ll Type) -> Vec<&'ll Type> { + unsafe { + let n_args = llvm::LLVMCountStructElementTypes(ty) as usize; + let mut args = Vec::with_capacity(n_args); + llvm::LLVMGetStructElementTypes(ty, args.as_mut_ptr()); + args.set_len(n_args); + args + } + } } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn type_bool(&self) -> &'ll Type { diff --git a/tests/codegen-llvm/inject-autocast.rs b/tests/codegen-llvm/inject-autocast.rs new file mode 100644 index 0000000000000..d79779285889c --- /dev/null +++ b/tests/codegen-llvm/inject-autocast.rs @@ -0,0 +1,39 @@ +//@ compile-flags: -C opt-level=0 -C target-feature=+kl +//@ only-x86_64 + +#![feature(link_llvm_intrinsics, abi_unadjusted, simd_ffi, portable_simd)] +#![crate_type = "lib"] + +use std::simd::i64x2; + +#[repr(C, packed)] +pub struct Bar(u32, i64x2, i64x2, i64x2, i64x2, i64x2, i64x2); +// CHECK: %Bar = type <{ i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> }> + +// CHECK-LABEL: @struct_autocast +#[no_mangle] +pub unsafe fn struct_autocast(key_metadata: u32, key: i64x2) -> Bar { + extern "unadjusted" { + #[link_name = "llvm.x86.encodekey128"] + fn foo(key_metadata: u32, key: i64x2) -> Bar; + } + + // CHECK: [[A:%[0-9]+]] = call { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32 {{.*}}, <2 x i64> {{.*}}) + // CHECK: [[B:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 0 + // CHECK: [[C:%[0-9]+]] = insertvalue %Bar poison, i32 [[B]], 0 + // CHECK: [[D:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 1 + // CHECK: [[E:%[0-9]+]] = insertvalue %Bar [[C]], <2 x i64> [[D]], 1 + // CHECK: [[F:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 2 + // CHECK: [[G:%[0-9]+]] = insertvalue %Bar [[E]], <2 x i64> [[F]], 2 + // CHECK: [[H:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 3 + // CHECK: [[I:%[0-9]+]] = insertvalue %Bar [[G]], <2 x i64> [[H]], 3 + // CHECK: [[J:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 4 + // CHECK: [[K:%[0-9]+]] = insertvalue %Bar [[I]], <2 x i64> [[J]], 4 + // CHECK: [[L:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 5 + // CHECK: [[M:%[0-9]+]] = insertvalue %Bar [[K]], <2 x i64> [[L]], 5 + // CHECK: [[N:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 6 + // CHECK: insertvalue %Bar [[M]], <2 x i64> [[N]], 6 + foo(key_metadata, key) +} + +// CHECK: declare { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32, <2 x i64>) From 5aa800af80169781d358e02fb82a5beaaead8159 Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 23:09:41 +0530 Subject: [PATCH 27/33] Add autocast for `i1` vectors --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 79 ++++++++++++++++---- tests/codegen-llvm/inject-autocast.rs | 41 +++++++++- 2 files changed, 105 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 6aedb6d97d0f8..70f145a7155b7 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1004,22 +1004,31 @@ fn can_autocast<'ll>(cx: &CodegenCx<'ll, '_>, rust_ty: &'ll Type, llvm_ty: &'ll return true; } - // Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust - // due to auto field-alignment in non-packed structs (packed structs are represented in LLVM - // as, well, packed structs, so they won't match with those either) - if cx.type_kind(llvm_ty) == TypeKind::Struct && cx.type_kind(rust_ty) == TypeKind::Struct { - let rust_element_tys = cx.struct_element_types(rust_ty); - let llvm_element_tys = cx.struct_element_types(llvm_ty); - - if rust_element_tys.len() != llvm_element_tys.len() { - return false; + match cx.type_kind(llvm_ty) { + // Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust + // due to auto field-alignment in non-packed structs (packed structs are represented in LLVM + // as, well, packed structs, so they won't match with those either) + TypeKind::Struct if cx.type_kind(rust_ty) == TypeKind::Struct => { + let rust_element_tys = cx.struct_element_types(rust_ty); + let llvm_element_tys = cx.struct_element_types(llvm_ty); + + if rust_element_tys.len() != llvm_element_tys.len() { + return false; + } + + iter::zip(rust_element_tys, llvm_element_tys).all( + |(rust_element_ty, llvm_element_ty)| { + can_autocast(cx, rust_element_ty, llvm_element_ty) + }, + ) } + TypeKind::Vector if cx.element_type(llvm_ty) == cx.type_i1() => { + let element_count = cx.vector_length(llvm_ty) as u64; + let int_width = element_count.next_power_of_two().max(8); - iter::zip(rust_element_tys, llvm_element_tys).all(|(rust_element_ty, llvm_element_ty)| { - can_autocast(cx, rust_element_ty, llvm_element_ty) - }) - } else { - false + rust_ty == cx.type_ix(int_width) + } + _ => false, } } @@ -1046,6 +1055,48 @@ fn autocast<'ll>( } ret } + // cast from the i1xN vector type to the primitive type + (TypeKind::Vector, TypeKind::Integer) if bx.element_type(src_ty) == bx.type_i1() => { + let vector_length = bx.vector_length(src_ty) as u64; + let int_width = vector_length.next_power_of_two().max(8); + + let val = if vector_length == int_width { + val + } else { + // zero-extends vector + let shuffle_indices = match vector_length { + 0 => unreachable!("zero length vectors are not allowed"), + 1 => vec![0, 1, 1, 1, 1, 1, 1, 1], + 2 => vec![0, 1, 2, 2, 2, 2, 2, 2], + 3 => vec![0, 1, 2, 3, 3, 3, 3, 3], + 4.. => (0..int_width as i32).collect(), + }; + let shuffle_mask = + shuffle_indices.into_iter().map(|i| bx.const_i32(i)).collect::>(); + bx.shuffle_vector(val, bx.const_null(src_ty), bx.const_vector(&shuffle_mask)) + }; + bx.bitcast(val, dest_ty) + } + // cast from the primitive type to the i1xN vector type + (TypeKind::Integer, TypeKind::Vector) if bx.element_type(dest_ty) == bx.type_i1() => { + let vector_length = bx.vector_length(dest_ty) as u64; + let int_width = vector_length.next_power_of_two().max(8); + + let intermediate_ty = bx.type_vector(bx.type_i1(), int_width); + let intermediate = bx.bitcast(val, intermediate_ty); + + if vector_length == int_width { + intermediate + } else { + let shuffle_mask: Vec<_> = + (0..vector_length).map(|i| bx.const_i32(i as i32)).collect(); + bx.shuffle_vector( + intermediate, + bx.const_poison(intermediate_ty), + bx.const_vector(&shuffle_mask), + ) + } + } _ => unreachable!(), } } diff --git a/tests/codegen-llvm/inject-autocast.rs b/tests/codegen-llvm/inject-autocast.rs index d79779285889c..ae5bd0e422998 100644 --- a/tests/codegen-llvm/inject-autocast.rs +++ b/tests/codegen-llvm/inject-autocast.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -C opt-level=0 -C target-feature=+kl +//@ compile-flags: -C opt-level=0 -C target-feature=+kl,+avx512vp2intersect,+avx512vl //@ only-x86_64 #![feature(link_llvm_intrinsics, abi_unadjusted, simd_ffi, portable_simd)] @@ -36,4 +36,43 @@ pub unsafe fn struct_autocast(key_metadata: u32, key: i64x2) -> Bar { foo(key_metadata, key) } +// CHECK-LABEL: @struct_with_i1_vector_autocast +#[no_mangle] +pub unsafe fn struct_with_i1_vector_autocast(a: i64x2, b: i64x2) -> (u8, u8) { + extern "unadjusted" { + #[link_name = "llvm.x86.avx512.vp2intersect.q.128"] + fn foo(a: i64x2, b: i64x2) -> (u8, u8); + } + + // CHECK: [[A:%[0-9]+]] = call { <2 x i1>, <2 x i1> } @llvm.x86.avx512.vp2intersect.q.128(<2 x i64> {{.*}}, <2 x i64> {{.*}}) + // CHECK: [[B:%[0-9]+]] = extractvalue { <2 x i1>, <2 x i1> } [[A]], 0 + // CHECK: [[C:%[0-9]+]] = shufflevector <2 x i1> [[B]], <2 x i1> zeroinitializer, <8 x i32> + // CHECK: [[D:%[0-9]+]] = bitcast <8 x i1> [[C]] to i8 + // CHECK: [[E:%[0-9]+]] = insertvalue { i8, i8 } poison, i8 [[D]], 0 + // CHECK: [[F:%[0-9]+]] = extractvalue { <2 x i1>, <2 x i1> } [[A]], 1 + // CHECK: [[G:%[0-9]+]] = shufflevector <2 x i1> [[F]], <2 x i1> zeroinitializer, <8 x i32> + // CHECK: [[H:%[0-9]+]] = bitcast <8 x i1> [[G]] to i8 + // CHECK: insertvalue { i8, i8 } [[E]], i8 [[H]], 1 + foo(a, b) +} + +// CHECK-LABEL: @i1_vector_autocast +#[no_mangle] +pub unsafe fn i1_vector_autocast(a: u8, b: u8) -> u8 { + extern "unadjusted" { + #[link_name = "llvm.x86.avx512.kadd.b"] + fn foo(a: u8, b: u8) -> u8; + } + + // CHECK: [[A:%[0-9]+]] = bitcast i8 {{.*}} to <8 x i1> + // CHECK: [[B:%[0-9]+]] = bitcast i8 {{.*}} to <8 x i1> + // CHECK: [[C:%[0-9]+]] = call <8 x i1> @llvm.x86.avx512.kadd.b(<8 x i1> [[A]], <8 x i1> [[B]]) + // CHECK: bitcast <8 x i1> [[C]] to i8 + foo(a, b) +} + // CHECK: declare { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32, <2 x i64>) + +// CHECK: declare { <2 x i1>, <2 x i1> } @llvm.x86.avx512.vp2intersect.q.128(<2 x i64>, <2 x i64>) + +// CHECK: declare <8 x i1> @llvm.x86.avx512.kadd.b(<8 x i1>, <8 x i1>) From 11f350da386e3fa51c106c09db1cf4d637754a9c Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 23:28:37 +0530 Subject: [PATCH 28/33] Add autocast for `bf16` and `bf16xN` --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 16 ++++++++++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 +++ compiler/rustc_codegen_llvm/src/type_.rs | 4 ++++ tests/codegen-llvm/inject-autocast.rs | 19 +++++++++++++++++-- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 70f145a7155b7..d46672bdffb7f 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1022,12 +1022,20 @@ fn can_autocast<'ll>(cx: &CodegenCx<'ll, '_>, rust_ty: &'ll Type, llvm_ty: &'ll }, ) } - TypeKind::Vector if cx.element_type(llvm_ty) == cx.type_i1() => { + TypeKind::Vector => { + let llvm_element_ty = cx.element_type(llvm_ty); let element_count = cx.vector_length(llvm_ty) as u64; - let int_width = element_count.next_power_of_two().max(8); - rust_ty == cx.type_ix(int_width) + if llvm_element_ty == cx.type_bf16() { + rust_ty == cx.type_vector(cx.type_i16(), element_count) + } else if llvm_element_ty == cx.type_i1() { + let int_width = element_count.next_power_of_two().max(8); + rust_ty == cx.type_ix(int_width) + } else { + false + } } + TypeKind::BFloat => rust_ty == cx.type_i16(), _ => false, } } @@ -1097,7 +1105,7 @@ fn autocast<'ll>( ) } } - _ => unreachable!(), + _ => bx.bitcast(val, dest_ty), // for `bf16(xN)` <-> `u16(xN)` } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index deafa38b7be64..525d1dbe9d0d3 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -921,6 +921,9 @@ unsafe extern "C" { pub(crate) fn LLVMDoubleTypeInContext(C: &Context) -> &Type; pub(crate) fn LLVMFP128TypeInContext(C: &Context) -> &Type; + // Operations on non-IEEE real types + pub(crate) fn LLVMBFloatTypeInContext(C: &Context) -> &Type; + // Operations on function types pub(crate) fn LLVMFunctionType<'a>( ReturnType: &'a Type, diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 147056a5885a6..796f3d9ef60ba 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -183,6 +183,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { ) } } + + pub(crate) fn type_bf16(&self) -> &'ll Type { + unsafe { llvm::LLVMBFloatTypeInContext(self.llcx()) } + } } impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { diff --git a/tests/codegen-llvm/inject-autocast.rs b/tests/codegen-llvm/inject-autocast.rs index ae5bd0e422998..fec9d3f0b1955 100644 --- a/tests/codegen-llvm/inject-autocast.rs +++ b/tests/codegen-llvm/inject-autocast.rs @@ -1,10 +1,10 @@ -//@ compile-flags: -C opt-level=0 -C target-feature=+kl,+avx512vp2intersect,+avx512vl +//@ compile-flags: -C opt-level=0 -C target-feature=+kl,+avx512vp2intersect,+avx512vl,+avxneconvert //@ only-x86_64 #![feature(link_llvm_intrinsics, abi_unadjusted, simd_ffi, portable_simd)] #![crate_type = "lib"] -use std::simd::i64x2; +use std::simd::{f32x4, i16x8, i64x2}; #[repr(C, packed)] pub struct Bar(u32, i64x2, i64x2, i64x2, i64x2, i64x2, i64x2); @@ -71,8 +71,23 @@ pub unsafe fn i1_vector_autocast(a: u8, b: u8) -> u8 { foo(a, b) } +// CHECK-LABEL: @bf16_vector_autocast +#[no_mangle] +pub unsafe fn bf16_vector_autocast(a: f32x4) -> i16x8 { + extern "unadjusted" { + #[link_name = "llvm.x86.vcvtneps2bf16128"] + fn foo(a: f32x4) -> i16x8; + } + + // CHECK: [[A:%[0-9]+]] = call <8 x bfloat> @llvm.x86.vcvtneps2bf16128(<4 x float> {{.*}}) + // CHECK: bitcast <8 x bfloat> [[A]] to <8 x i16> + foo(a) +} + // CHECK: declare { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32, <2 x i64>) // CHECK: declare { <2 x i1>, <2 x i1> } @llvm.x86.avx512.vp2intersect.q.128(<2 x i64>, <2 x i64>) // CHECK: declare <8 x i1> @llvm.x86.avx512.kadd.b(<8 x i1>, <8 x i1>) + +// CHECK: declare <8 x bfloat> @llvm.x86.vcvtneps2bf16128(<4 x float>) From 84f4b803f784ebb7aee759d0facc1f341768acd7 Mon Sep 17 00:00:00 2001 From: BoxyUwU Date: Sun, 12 Apr 2026 15:48:50 -0400 Subject: [PATCH 29/33] Release notes for 1.95 --- RELEASES.md | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index c396cd8069d6d..c1cf337ea8d2a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,134 @@ +Version 1.95 (2026-04-16) +========================== + + + +Language +-------- +- [Stabilize `if let` guards on match arms](https://github.com/rust-lang/rust/pull/141295) +- [`irrefutable_let_patterns` lint no longer lints on let chains](https://github.com/rust-lang/rust/pull/146832) +- [Support importing path-segment keywords with renaming](https://github.com/rust-lang/rust/pull/146972) +- [Stabilize inline assembly for PowerPC and PowerPC64](https://github.com/rust-lang/rust/pull/147996) +- [const-eval: be more consistent in the behavior of padding during typed copies](https://github.com/rust-lang/rust/pull/148967) +- [Const blocks are no longer evaluated to determine if expressions involving fallible operations can implicitly be constant-promoted.](https://github.com/rust-lang/rust/pull/150557). Expressions whose ability to implicitly be promoted would depend on the result of a const block are no longer implicitly promoted. +- [Make operational semantics of pattern matching independent of crate and module](https://github.com/rust-lang/rust/pull/150681) + + + + +Compiler +-------- +- [Stabilize `--remap-path-scope` for controlling the scoping of how paths get remapped in the resulting binary](https://github.com/rust-lang/rust/pull/147611) + + + + +Platform Support +---------------- +- [Promote `powerpc64-unknown-linux-musl` to Tier 2 with host tools](https://github.com/rust-lang/rust/pull/149962) +- [Promote `aarch64-apple-tvos` to Tier 2](https://github.com/rust-lang/rust/pull/152021) +- [Promote `aarch64-apple-tvos-sim` to Tier 2](https://github.com/rust-lang/rust/pull/152021) +- [Promote `aarch64-apple-watchos` to Tier 2](https://github.com/rust-lang/rust/pull/152021) +- [Promote `aarch64-apple-watchos-sim` to Tier 2](https://github.com/rust-lang/rust/pull/152021) +- [Promote `aarch64-apple-visionos` to Tier 2](https://github.com/rust-lang/rust/pull/152021) +- [Promote `aarch64-apple-visionos-sim` to Tier 2](https://github.com/rust-lang/rust/pull/152021) + + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html + + + +Libraries +--------- +- [`thread::scope`: document how join interacts with TLS destructors](https://github.com/rust-lang/rust/pull/149482) +- [Speed up `str::contains` on aarch64 targets with `neon` target feature enabled by default](https://github.com/rust-lang/rust/pull/152176) + + + + +Stabilized APIs +--------------- + +- [`MaybeUninit<[T; N]>: From<[MaybeUninit; N]>`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#impl-From%3CMaybeUninit%3C%5BT;+N%5D%3E%3E-for-%5BMaybeUninit%3CT%3E;+N%5D) +- [`MaybeUninit<[T; N]>: AsRef<[MaybeUninit; N]>`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#impl-AsRef%3C%5BMaybeUninit%3CT%3E;+N%5D%3E-for-MaybeUninit%3C%5BT;+N%5D%3E) +- [`MaybeUninit<[T; N]>: AsRef<[MaybeUninit]>`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#impl-AsRef%3C%5BMaybeUninit%3CT%3E%5D%3E-for-MaybeUninit%3C%5BT;+N%5D%3E) +- [`MaybeUninit<[T; N]>: AsMut<[MaybeUninit; N]>`](https://doc.rust-lang.org/beta/std/mem/union.MaybeUninit.html#impl-AsMut%3C%5BMaybeUninit%3CT%3E;+N%5D%3E-for-MaybeUninit%3C%5BT;+N%5D%3E) +- [`MaybeUninit<[T; N]>: AsMut<[MaybeUninit]>`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#impl-AsMut%3C%5BMaybeUninit%3CT%3E%5D%3E-for-MaybeUninit%3C%5BT;+N%5D%3E) +- [`[MaybeUninit; N]: From>`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#impl-From%3C%5BMaybeUninit%3CT%3E;+N%5D%3E-for-MaybeUninit%3C%5BT;+N%5D%3E) +- [`Cell<[T; N]>: AsRef<[Cell; N]>`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#impl-AsRef%3C%5BCell%3CT%3E;+N%5D%3E-for-Cell%3C%5BT;+N%5D%3E) +- [`Cell<[T; N]>: AsRef<[Cell]>`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#impl-AsRef%3C%5BCell%3CT%3E%5D%3E-for-Cell%3C%5BT;+N%5D%3E) +- [`Cell<[T]>: AsRef<[Cell]>`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#impl-AsRef%3C%5BCell%3CT%3E%5D%3E-for-Cell%3C%5BT%5D%3E) +- [`bool: TryFrom<{integer}>`](https://doc.rust-lang.org/stable/std/primitive.bool.html#impl-TryFrom%3Cu128%3E-for-bool) +- [`AtomicPtr::update`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.update) +- [`AtomicPtr::try_update`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicPtr.html#method.try_update) +- [`AtomicBool::update`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicBool.html#method.update) +- [`AtomicBool::try_update`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicBool.html#method.try_update) +- [`AtomicIn::update`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicIsize.html#method.update) +- [`AtomicIn::try_update`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicIsize.html#method.try_update) +- [`AtomicUn::update`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicUsize.html#method.update) +- [`AtomicUn::try_update`](https://doc.rust-lang.org/stable/std/sync/atomic/struct.AtomicUsize.html#method.try_update) +- [`cfg_select!`](https://doc.rust-lang.org/stable/std/macro.cfg_select.html) +- [`mod core::range`](https://doc.rust-lang.org/stable/core/range/index.html) +- [`core::range::RangeInclusive`](https://doc.rust-lang.org/stable/core/range/struct.RangeInclusive.html) +- [`core::range::RangeInclusiveIter`](https://doc.rust-lang.org/stable/core/range/struct.RangeInclusiveIter.html) +- [`core::hint::cold_path`](https://doc.rust-lang.org/stable/core/hint/fn.cold_path.html) +- [`<*const T>::as_ref_unchecked`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_ref_unchecked) +- [`<*mut T>::as_ref_unchecked`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_ref_unchecked-1) +- [`<*mut T>::as_mut_unchecked`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut_unchecked) + + +These previously stable APIs are now stable in const contexts: + +- [`fmt::from_fn`](https://doc.rust-lang.org/stable/std/fmt/fn.from_fn.html) +- [`ControlFlow::is_break`](https://doc.rust-lang.org/stable/core/ops/enum.ControlFlow.html#method.is_break) +- [`ControlFlow::is_continue`](https://doc.rust-lang.org/stable/core/ops/enum.ControlFlow.html#method.is_continue) + + + + +Cargo +----- +- [docs(report): enhance man pages for `cargo report *`](https://github.com/rust-lang/cargo/pull/16430/) + +Rustdoc +----- +- [In search results, rank unstable items lower](https://github.com/rust-lang/rust/pull/149460) +- [Add new "hide deprecated items" setting in rustdoc](https://github.com/rust-lang/rust/pull/151091) + + +Compatibility Notes +------------------- +- [Array coercions may now result in less inference constraints than before](https://github.com/rust-lang/rust/pull/140283) +- Importing `$crate` without renaming, i.e. `use $crate::{self};`, is now no longer permitted due to stricter error checking for `self` imports. +- [const-eval: be more consistent in the behavior of padding during typed copies.](https://github.com/rust-lang/rust/pull/148967) + In very rare cases, this may cause compilation errors due to bytes from parts of a pointer ending up in the padding bytes of a `const` or `static`. +- [A future-incompatibility warning lint `ambiguous_glob_imported_traits` is now reported when using an ambiguously glob imported trait](https://github.com/rust-lang/rust/pull/149058) +- [Check lifetime bounds of types mentioning only type parameters](https://github.com/rust-lang/rust/pull/149389) +- [Report more visibility-related ambiguous import errors](https://github.com/rust-lang/rust/pull/149596) +- [Deprecate `Eq::assert_receiver_is_total_eq` and emit future compatibility warnings on manual impls](https://github.com/rust-lang/rust/pull/149978) +- [powerpc64: Use the ELF ABI version set in target spec instead of guessing](https://github.com/rust-lang/rust/pull/150468) (fixes the ELF ABI used by the OpenBSD target) +- Matching on a `#[non_exhaustive]` enum [now reads the discriminant, even if the enum has only one variant](https://github.com/rust-lang/rust/pull/150681). This can cause closures to capture values that they previously wouldn't. +- `mut ref` and `mut ref mut` patterns, part of the unstable [Match Ergonomics 2024 RFC](https://github.com/rust-lang/rust/issues/123076), were accidentally allowed on stable within struct pattern field shorthand. These patterns are now correctly feature-gated as unstable in this position. +- [Add future-compatibility warning for derive helper attributes which conflict with built-in attributes](https://github.com/rust-lang/rust/pull/151152) +- [JSON target specs](https://doc.rust-lang.org/rustc/targets/custom.html) have been destabilized and now require `-Z unstable-options` to use. Previously, they could not be used without the standard library, which has no stable build mechanism. In preparation for the `build-std` project adding that support, JSON target specs are being proactively gated to ensure they remain unstable even if `build-std` is stabilized. Cargo now includes the `-Z json-target-spec` CLI flag to automatically pass `-Z unstable-options` to the compiler when needed. See [#150151](https://github.com/rust-lang/rust/pull/150151), [#151534](https://github.com/rust-lang/rust/pull/150151), and [rust-lang/cargo#16557](https://github.com/rust-lang/cargo/pull/16557). +- [The arguments of `#[feature]` attributes on invalid targets are now checked](https://github.com/rust-lang/rust/issues/153764) + + + + +Internal Changes +---------------- + +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. + +- [Update to LLVM 22](https://github.com/rust-lang/rust/pull/150722) + + Version 1.94.1 (2026-03-26) =========================== From 3f34b2c239064d49cc0db22172726e4bb12212ab Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 18 Mar 2026 16:49:58 +0300 Subject: [PATCH 30/33] ast: Preserve the star symbol span in glob delegation items --- compiler/rustc_ast/src/ast.rs | 9 +++++++-- compiler/rustc_ast/src/visit.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state/item.rs | 10 ++++++++-- compiler/rustc_expand/src/expand.rs | 8 ++++---- compiler/rustc_parse/src/parser/item.rs | 13 +++++++++---- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ae4989fcbc6c9..c68b1e92550a0 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3901,12 +3901,17 @@ pub struct Delegation { pub from_glob: bool, } +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub enum DelegationSuffixes { + List(ThinVec<(Ident, Option)>), + Glob(Span), +} + #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct DelegationMac { pub qself: Option>, pub prefix: Path, - // Some for list delegation, and None for glob delegation. - pub suffixes: Option)>>, + pub suffixes: DelegationSuffixes, pub body: Option>, } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 6aa8d5f38ad24..ee4b1d1354300 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -430,6 +430,7 @@ macro_rules! common_visitor_and_walkers { Defaultness, Delegation, DelegationMac, + DelegationSuffixes, DelimArgs, DelimSpan, EnumDef, diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 3e9c596148343..aec59908d5b64 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -435,7 +435,10 @@ impl<'a> State<'a> { &item.vis, &deleg.qself, &deleg.prefix, - deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), + match &deleg.suffixes { + ast::DelegationSuffixes::List(s) => DelegationKind::List(s), + ast::DelegationSuffixes::Glob(_) => DelegationKind::Glob, + }, &deleg.body, ), } @@ -641,7 +644,10 @@ impl<'a> State<'a> { vis, &deleg.qself, &deleg.prefix, - deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), + match &deleg.suffixes { + ast::DelegationSuffixes::List(s) => DelegationKind::List(s), + ast::DelegationSuffixes::Glob(_) => DelegationKind::Glob, + }, &deleg.body, ), } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 9f5a01452fdc3..92efab3212ab3 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -8,9 +8,9 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrItemKind, AttrStyle, AttrVec, - DUMMY_NODE_ID, EarlyParsedAttribute, ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, - ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, NodeId, PatKind, StmtKind, - TyKind, token, + DUMMY_NODE_ID, DelegationSuffixes, EarlyParsedAttribute, ExprKind, ForeignItemKind, HasAttrs, + HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, NodeId, + PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; use rustc_attr_parsing::parser::AllowExprMetavar; @@ -2401,7 +2401,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { res } None if let Some((deleg, item)) = node.delegation() => { - let Some(suffixes) = &deleg.suffixes else { + let DelegationSuffixes::List(suffixes) = &deleg.suffixes else { let traitless_qself = matches!(&deleg.qself, Some(qself) if qself.position == 0); let (item, of_trait) = match node.to_annotatable() { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index ab3683f598208..e3072070871ba 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -857,7 +857,7 @@ impl<'a> Parser<'a> { kind: AssocItemKind::DelegationMac(Box::new(DelegationMac { qself: None, prefix: of_trait.trait_ref.path.clone(), - suffixes: None, + suffixes: DelegationSuffixes::Glob(whole_reuse_span), body, })), })); @@ -879,10 +879,12 @@ impl<'a> Parser<'a> { Ok(if self.eat_path_sep() { let suffixes = if self.eat(exp!(Star)) { - None + DelegationSuffixes::Glob(self.prev_token.span) } else { let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?)); - Some(self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0) + DelegationSuffixes::List( + self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0, + ) }; ItemKind::DelegationMac(Box::new(DelegationMac { @@ -1517,7 +1519,10 @@ impl<'a> Parser<'a> { let span = self.psess.source_map().guess_head_span(span); let descr = kind.descr(); let help = match kind { - ItemKind::DelegationMac(deleg) if deleg.suffixes.is_none() => false, + ItemKind::DelegationMac(box DelegationMac { + suffixes: DelegationSuffixes::Glob(_), + .. + }) => false, _ => true, }; self.dcx().emit_err(errors::BadItemKind { span, descr, ctx, help }); From bde90656db28b59b8e577299395022895cfce6ec Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 18 Mar 2026 17:30:57 +0300 Subject: [PATCH 31/33] expand: More precise location for glob delegation The span location of the last segment in the desugared path is inherited from the star symbol's span --- compiler/rustc_expand/src/base.rs | 11 +++++++-- compiler/rustc_resolve/src/macros.rs | 23 ++++++++++++------- .../delegation/impl-reuse-negative-traits.rs | 2 +- .../impl-reuse-negative-traits.stderr | 6 ++--- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 7fd891395fa02..fd685f534da18 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1019,18 +1019,24 @@ impl SyntaxExtension { pub fn glob_delegation( trait_def_id: DefId, impl_def_id: LocalDefId, + star_span: Span, edition: Edition, ) -> SyntaxExtension { struct GlobDelegationExpanderImpl { trait_def_id: DefId, impl_def_id: LocalDefId, + star_span: Span, } impl GlobDelegationExpander for GlobDelegationExpanderImpl { fn expand( &self, ecx: &mut ExtCtxt<'_>, ) -> ExpandResult)>, ()> { - match ecx.resolver.glob_delegation_suffixes(self.trait_def_id, self.impl_def_id) { + match ecx.resolver.glob_delegation_suffixes( + self.trait_def_id, + self.impl_def_id, + self.star_span, + ) { Ok(suffixes) => ExpandResult::Ready(suffixes), Err(Indeterminate) if ecx.force_mode => ExpandResult::Ready(Vec::new()), Err(Indeterminate) => ExpandResult::Retry(()), @@ -1038,7 +1044,7 @@ impl SyntaxExtension { } } - let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id }; + let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id, star_span }; SyntaxExtension::default(SyntaxExtensionKind::GlobDelegation(Arc::new(expander)), edition) } @@ -1170,6 +1176,7 @@ pub trait ResolverExpand { &self, trait_def_id: DefId, impl_def_id: LocalDefId, + star_span: Span, ) -> Result)>, Indeterminate>; /// Record the name of an opaque `Ty::ImplTrait` pre-expansion so that it can be used diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 67a896bdd7557..e43efd64523c3 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -4,7 +4,7 @@ use std::mem; use std::sync::Arc; -use rustc_ast::{self as ast, Crate, DUMMY_NODE_ID, NodeId}; +use rustc_ast::{self as ast, Crate, DUMMY_NODE_ID, DelegationSuffixes, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; @@ -286,7 +286,8 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive), InvocationKind::GlobDelegation { ref item, .. } => { let ast::AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() }; - deleg_impl = Some(self.invocation_parent(invoc_id)); + let DelegationSuffixes::Glob(star_span) = deleg.suffixes else { unreachable!() }; + deleg_impl = Some((self.invocation_parent(invoc_id), star_span)); // It is sufficient to consider glob delegation a bang macro for now. (&deleg.prefix, MacroKind::Bang) } @@ -530,6 +531,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { &self, trait_def_id: DefId, impl_def_id: LocalDefId, + star_span: Span, ) -> Result)>, Indeterminate> { let target_trait = self.expect_module(trait_def_id); if !target_trait.unexpanded_invocations.borrow().is_empty() { @@ -549,13 +551,13 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let mut idents = Vec::new(); target_trait.for_each_child(self, |this, ident, orig_ident_span, ns, _binding| { - // FIXME: Adjust hygiene for idents from globs, like for glob imports. if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id) && overriding_keys.contains(&BindingKey::new(ident, ns)) { // The name is overridden, do not produce it from the glob delegation. } else { - idents.push((ident.orig(orig_ident_span), None)); + // FIXME: Adjust hygiene for idents from globs, like for glob imports. + idents.push((ident.orig(star_span.with_ctxt(orig_ident_span.ctxt())), None)); } }); Ok(idents) @@ -579,7 +581,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope: &ParentScope<'ra>, node_id: NodeId, force: bool, - deleg_impl: Option, + deleg_impl: Option<(LocalDefId, Span)>, invoc_in_mod_inert_attr: Option, suggestion_span: Option, ) -> Result<(Arc, Res), Indeterminate> { @@ -771,7 +773,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { kind: MacroKind, parent_scope: &ParentScope<'ra>, force: bool, - deleg_impl: Option, + deleg_impl: Option<(LocalDefId, Span)>, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, ignore_import: Option>, suggestion_span: Option, @@ -856,10 +858,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let res = res?; let ext = match deleg_impl { - Some(impl_def_id) => match res { + Some((impl_def_id, star_span)) => match res { def::Res::Def(DefKind::Trait, def_id) => { let edition = self.tcx.sess.edition(); - Some(Arc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition))) + Some(Arc::new(SyntaxExtension::glob_delegation( + def_id, + impl_def_id, + star_span, + edition, + ))) } _ => None, }, diff --git a/tests/ui/delegation/impl-reuse-negative-traits.rs b/tests/ui/delegation/impl-reuse-negative-traits.rs index 7bcbc82f03db8..8bc2b72708599 100644 --- a/tests/ui/delegation/impl-reuse-negative-traits.rs +++ b/tests/ui/delegation/impl-reuse-negative-traits.rs @@ -4,7 +4,6 @@ trait Trait { fn foo(&self); - //~^ ERROR negative impls cannot have any items [E0749] } struct S; @@ -15,5 +14,6 @@ impl Trait for S { struct F(S); reuse impl !Trait for F { &self.0 } +//~^ ERROR negative impls cannot have any items fn main() {} diff --git a/tests/ui/delegation/impl-reuse-negative-traits.stderr b/tests/ui/delegation/impl-reuse-negative-traits.stderr index 1be6ef715920d..7510fdd89d7c6 100644 --- a/tests/ui/delegation/impl-reuse-negative-traits.stderr +++ b/tests/ui/delegation/impl-reuse-negative-traits.stderr @@ -1,8 +1,8 @@ error[E0749]: negative impls cannot have any items - --> $DIR/impl-reuse-negative-traits.rs:6:8 + --> $DIR/impl-reuse-negative-traits.rs:16:1 | -LL | fn foo(&self); - | ^^^ +LL | reuse impl !Trait for F { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error From 32d9f473dbc785886bd43ab302bed215300da1ff Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 18 Mar 2026 19:05:12 +0300 Subject: [PATCH 32/33] delegation: Give declaration of `self` syntax context of the delegation body Instead of the last segment of the delegation path. `self` is something that introduced by the whole delegation item, not some specific part of it, and the last segment may need to have a different context for path resolution purposes. --- compiler/rustc_resolve/src/late.rs | 3 +- tests/ui/delegation/impl-reuse-pass.rs | 25 ------- .../ui/delegation/impl-reuse-self-hygiene.rs | 28 ++++++++ .../delegation/impl-reuse-self-hygiene.stderr | 68 +++++++++++++++++++ 4 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 tests/ui/delegation/impl-reuse-self-hygiene.rs create mode 100644 tests/ui/delegation/impl-reuse-self-hygiene.stderr diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2c287045be7a9..77f845bed76d3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3873,8 +3873,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let Some(body) = &delegation.body else { return }; self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { - let span = delegation.path.segments.last().unwrap().ident.span; - let ident = Ident::new(kw::SelfLower, span.normalize_to_macro_rules()); + let ident = Ident::new(kw::SelfLower, body.span.normalize_to_macro_rules()); let res = Res::Local(delegation.id); this.innermost_rib_bindings(ValueNS).insert(ident, res); diff --git a/tests/ui/delegation/impl-reuse-pass.rs b/tests/ui/delegation/impl-reuse-pass.rs index 90060b03f9efd..578e8c08e7038 100644 --- a/tests/ui/delegation/impl-reuse-pass.rs +++ b/tests/ui/delegation/impl-reuse-pass.rs @@ -173,19 +173,6 @@ mod macros { macro_rules! m { () => { M } } reuse impl Trait for m!() { self_0_ref!(self) } - struct S1(u8); - macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } - one_line_reuse!(self); - - struct S2(u8); - macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } - one_line_reuse_expr!(self.0); - - struct S3(u8); - macro_rules! s3 { () => { S3 } } - macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } - one_line_reuse_expr2!(self.0); - fn f() { let s = S(1); s.foo(); @@ -194,18 +181,6 @@ mod macros { let m = M(41); m.foo(); m.bar(); - - let s1 = S1(2); - s1.foo(); - s1.bar(); - - let s2 = S2(4); - s2.foo(); - s2.bar(); - - let s3 = S3(5); - s3.foo(); - s3.bar(); } } diff --git a/tests/ui/delegation/impl-reuse-self-hygiene.rs b/tests/ui/delegation/impl-reuse-self-hygiene.rs new file mode 100644 index 0000000000000..b49e4419703a7 --- /dev/null +++ b/tests/ui/delegation/impl-reuse-self-hygiene.rs @@ -0,0 +1,28 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +trait Trait { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +struct S1(u8); +macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } +//~^ ERROR expected value, found module `self` +//~| ERROR expected value, found module `self` +one_line_reuse!(self); + +struct S2(u8); +macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } +one_line_reuse_expr!(self.0); +//~^ ERROR expected value, found module `self` +//~| ERROR expected value, found module `self` + +struct S3(u8); +macro_rules! s3 { () => { S3 } } +macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } +one_line_reuse_expr2!(self.0); +//~^ ERROR expected value, found module `self` +//~| ERROR expected value, found module `self` + +fn main() {} diff --git a/tests/ui/delegation/impl-reuse-self-hygiene.stderr b/tests/ui/delegation/impl-reuse-self-hygiene.stderr new file mode 100644 index 0000000000000..ae93829809e4f --- /dev/null +++ b/tests/ui/delegation/impl-reuse-self-hygiene.stderr @@ -0,0 +1,68 @@ +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:10:76 + | +LL | macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } + | --------------------------^^^^^---- + | | | + | | `self` value is a keyword only available in methods with a `self` parameter + | `self` not allowed in an implementation +... +LL | one_line_reuse!(self); + | --------------------- in this macro invocation + | + = note: this error originates in the macro `one_line_reuse` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:10:76 + | +LL | macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } + | --------------------------^^^^^---- + | | | + | | `self` value is a keyword only available in methods with a `self` parameter + | `self` not allowed in an implementation +... +LL | one_line_reuse!(self); + | --------------------- in this macro invocation + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `one_line_reuse` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:17:22 + | +LL | macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } + | ------------------------------ `self` not allowed in an implementation +LL | one_line_reuse_expr!(self.0); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:17:22 + | +LL | macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } + | ------------------------------ `self` not allowed in an implementation +LL | one_line_reuse_expr!(self.0); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:24:23 + | +LL | macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } + | --------------------------------- `self` not allowed in an implementation +LL | one_line_reuse_expr2!(self.0); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:24:23 + | +LL | macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } + | --------------------------------- `self` not allowed in an implementation +LL | one_line_reuse_expr2!(self.0); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0424`. From a9c281928b827bd902c9f83fcb027a98b4cda522 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 8 Apr 2026 14:59:53 +0300 Subject: [PATCH 33/33] Update some tests after rebase --- .../generics/query-cycle-oom-154169.rs | 4 +- .../generics/query-cycle-oom-154169.stderr | 33 +++--------- tests/ui/delegation/glob-glob-conflict.rs | 9 ++-- tests/ui/delegation/glob-glob-conflict.stderr | 50 +++++++++---------- 4 files changed, 40 insertions(+), 56 deletions(-) diff --git a/tests/ui/delegation/generics/query-cycle-oom-154169.rs b/tests/ui/delegation/generics/query-cycle-oom-154169.rs index 04a2896f6dd21..0636f19a548ae 100644 --- a/tests/ui/delegation/generics/query-cycle-oom-154169.rs +++ b/tests/ui/delegation/generics/query-cycle-oom-154169.rs @@ -20,7 +20,6 @@ mod test_2 { trait Trait { fn foo() -> Self::Assoc; //~^ ERROR: associated type `Assoc` not found for `Self` - //~| ERROR: this function takes 0 arguments but 1 argument was supplied fn bar(&self) -> u8; } @@ -33,6 +32,7 @@ mod test_2 { impl Trait for S { reuse Trait::* { &self.0 } + //~^ ERROR this function takes 0 arguments but 1 argument was supplied fn bar(&self) -> u8 { 2 } } } @@ -40,13 +40,13 @@ mod test_2 { mod test_3 { trait Trait { fn foo(&self) -> Self::Assoc<3> { //~ ERROR: associated type `Assoc` not found for `Self` - //~^ ERROR: no method named `foo` found for reference `&()` in the current scope [(); 3] } } impl () { //~ ERROR: cannot define inherent `impl` for primitive types reuse Trait::*; + //~^ ERROR no method named `foo` found for reference `&()` in the current scope } } diff --git a/tests/ui/delegation/generics/query-cycle-oom-154169.stderr b/tests/ui/delegation/generics/query-cycle-oom-154169.stderr index 1baed6fd6748d..682736b7a998a 100644 --- a/tests/ui/delegation/generics/query-cycle-oom-154169.stderr +++ b/tests/ui/delegation/generics/query-cycle-oom-154169.stderr @@ -27,7 +27,7 @@ LL | fn foo(&self) -> Self::Assoc<3> { | ^^^^^ associated type `Assoc` not found error[E0046]: not all trait items implemented, missing: `foo` - --> $DIR/query-cycle-oom-154169.rs:27:5 + --> $DIR/query-cycle-oom-154169.rs:26:5 | LL | fn foo() -> Self::Assoc; | ------------------------ `foo` from trait @@ -36,7 +36,7 @@ LL | impl Trait for u8 { | ^^^^^^^^^^^^^^^^^ missing `foo` in implementation error[E0390]: cannot define inherent `impl` for primitive types - --> $DIR/query-cycle-oom-154169.rs:48:5 + --> $DIR/query-cycle-oom-154169.rs:47:5 | LL | impl () { | ^^^^^^^ @@ -52,13 +52,10 @@ LL | reuse to_reuse::foo { &self.0 } = help: you might be missing a crate named `to_reuse` error[E0061]: this function takes 0 arguments but 1 argument was supplied - --> $DIR/query-cycle-oom-154169.rs:21:12 + --> $DIR/query-cycle-oom-154169.rs:34:22 | -LL | fn foo() -> Self::Assoc; - | ^^^ -... LL | reuse Trait::* { &self.0 } - | ------- unexpected argument + | ^ ------- unexpected argument | note: associated function defined here --> $DIR/query-cycle-oom-154169.rs:21:12 @@ -67,29 +64,15 @@ LL | fn foo() -> Self::Assoc; | ^^^ help: remove the extra argument | -LL - fn foo() -> Self::Assoc; -LL - -LL - -LL - fn bar(&self) -> u8; -LL - } -LL - -LL - impl Trait for u8 { -LL - -LL - fn bar(&self) -> u8 { 1 } -LL - } -LL - -LL - struct S(u8); -LL - -LL - impl Trait for S { LL - reuse Trait::* { &self.0 } -LL + fn fo&self.0 } +LL + reuse Trait::&self.0 } | error[E0599]: no method named `foo` found for reference `&()` in the current scope - --> $DIR/query-cycle-oom-154169.rs:42:12 + --> $DIR/query-cycle-oom-154169.rs:48:22 | -LL | fn foo(&self) -> Self::Assoc<3> { - | ^^^ method not found in `&()` +LL | reuse Trait::*; + | ^ method not found in `&()` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following traits define an item `foo`, perhaps you need to implement one of them: diff --git a/tests/ui/delegation/glob-glob-conflict.rs b/tests/ui/delegation/glob-glob-conflict.rs index cb07a78b84fef..c1cd3f703e07f 100644 --- a/tests/ui/delegation/glob-glob-conflict.rs +++ b/tests/ui/delegation/glob-glob-conflict.rs @@ -3,13 +3,10 @@ trait Trait1 { fn method(&self) -> u8; - //~^ ERROR: this function takes 1 argument but 0 arguments were supplied - //~| ERROR: mismatched types } trait Trait2 { fn method(&self) -> u8; - //~^ ERROR: this function takes 1 argument but 0 arguments were supplied - //~| ERROR: mismatched types + } trait Trait { fn method(&self) -> u8; @@ -28,10 +25,14 @@ impl Trait2 for u8 { impl Trait for u8 { reuse Trait1::*; reuse Trait2::*; //~ ERROR duplicate definitions with name `method` + //~^ ERROR: this function takes 1 argument but 0 arguments were supplied + //~| ERROR: mismatched types } impl Trait for u16 { reuse Trait1::*; reuse Trait1::*; //~ ERROR duplicate definitions with name `method` + //~^ ERROR: this function takes 1 argument but 0 arguments were supplied + //~| ERROR: mismatched types } fn main() {} diff --git a/tests/ui/delegation/glob-glob-conflict.stderr b/tests/ui/delegation/glob-glob-conflict.stderr index 4259d71117b7b..f55ee333630c6 100644 --- a/tests/ui/delegation/glob-glob-conflict.stderr +++ b/tests/ui/delegation/glob-glob-conflict.stderr @@ -1,5 +1,5 @@ error[E0201]: duplicate definitions with name `method`: - --> $DIR/glob-glob-conflict.rs:30:5 + --> $DIR/glob-glob-conflict.rs:27:5 | LL | fn method(&self) -> u8; | ----------------------- item in trait @@ -10,7 +10,7 @@ LL | reuse Trait2::*; | ^^^^^^^^^^^^^^^^ duplicate definition error[E0201]: duplicate definitions with name `method`: - --> $DIR/glob-glob-conflict.rs:34:5 + --> $DIR/glob-glob-conflict.rs:33:5 | LL | fn method(&self) -> u8; | ----------------------- item in trait @@ -21,35 +21,35 @@ LL | reuse Trait1::*; | ^^^^^^^^^^^^^^^^ duplicate definition error[E0061]: this function takes 1 argument but 0 arguments were supplied - --> $DIR/glob-glob-conflict.rs:10:8 + --> $DIR/glob-glob-conflict.rs:27:19 | -LL | fn method(&self) -> u8; - | ^^^^^^ argument #1 of type `&_` is missing +LL | reuse Trait2::*; + | ^ argument #1 of type `&_` is missing | note: method defined here - --> $DIR/glob-glob-conflict.rs:10:8 + --> $DIR/glob-glob-conflict.rs:8:8 | LL | fn method(&self) -> u8; | ^^^^^^ ---- help: provide the argument | -LL | fn method(/* value */)(&self) -> u8; - | +++++++++++++ +LL | reuse Trait2::*(/* value */); + | +++++++++++++ error[E0308]: mismatched types - --> $DIR/glob-glob-conflict.rs:10:8 + --> $DIR/glob-glob-conflict.rs:27:19 | -LL | fn method(&self) -> u8; - | ^^^^^^- help: consider using a semicolon here: `;` - | | - | expected `()`, found `u8` - | expected `()` because of default return type +LL | reuse Trait2::*; + | ^- help: consider using a semicolon here: `;` + | | + | expected `()`, found `u8` + | expected `()` because of default return type error[E0061]: this function takes 1 argument but 0 arguments were supplied - --> $DIR/glob-glob-conflict.rs:5:8 + --> $DIR/glob-glob-conflict.rs:33:19 | -LL | fn method(&self) -> u8; - | ^^^^^^ argument #1 of type `&_` is missing +LL | reuse Trait1::*; + | ^ argument #1 of type `&_` is missing | note: method defined here --> $DIR/glob-glob-conflict.rs:5:8 @@ -58,17 +58,17 @@ LL | fn method(&self) -> u8; | ^^^^^^ ---- help: provide the argument | -LL | fn method(/* value */)(&self) -> u8; - | +++++++++++++ +LL | reuse Trait1::*(/* value */); + | +++++++++++++ error[E0308]: mismatched types - --> $DIR/glob-glob-conflict.rs:5:8 + --> $DIR/glob-glob-conflict.rs:33:19 | -LL | fn method(&self) -> u8; - | ^^^^^^- help: consider using a semicolon here: `;` - | | - | expected `()`, found `u8` - | expected `()` because of default return type +LL | reuse Trait1::*; + | ^- help: consider using a semicolon here: `;` + | | + | expected `()`, found `u8` + | expected `()` because of default return type error: aborting due to 6 previous errors