From c6cb2cc851ea1e39f9bd2390dedfca49bc761bd1 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 25 Mar 2026 19:05:06 +0300 Subject: [PATCH 01/16] 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 02/16] 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 03/16] 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 04/16] 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 e756d1607d6af1e8a76e5b1e7bf8821db990772e Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 26 Mar 2026 16:45:33 +0100 Subject: [PATCH 05/16] 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 06/16] 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 07/16] 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 08/16] 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 09/16] 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 10/16] 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 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 11/16] 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 12/16] 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 13/16] 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 14/16] 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 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 15/16] 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 84f4b803f784ebb7aee759d0facc1f341768acd7 Mon Sep 17 00:00:00 2001 From: BoxyUwU Date: Sun, 12 Apr 2026 15:48:50 -0400 Subject: [PATCH 16/16] 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) ===========================