From 1a3d919258ff51abc7dfb5ecf6fdc9b15ff8a8c2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 15 Jul 2026 12:14:08 +0200 Subject: [PATCH] Deny multiple EII impls on a single item This allows implementing EIIs that don't have a default impl without the involvement of symbol aliases by simply changing the mangled symbol name of the implementation, which would make them trivially compatible with all backends and targets. This change will be left to a followup PR. --- compiler/rustc_ast/src/ast.rs | 6 +- compiler/rustc_ast/src/visit.rs | 4 +- compiler/rustc_ast_lowering/src/item.rs | 24 +++-- .../rustc_ast_passes/src/ast_validation.rs | 30 +++---- .../rustc_ast_pretty/src/pprust/state/item.rs | 20 ++--- .../src/alloc_error_handler.rs | 2 +- compiler/rustc_builtin_macros/src/autodiff.rs | 2 +- .../src/deriving/generic/mod.rs | 2 +- .../rustc_builtin_macros/src/diagnostics.rs | 20 +++-- compiler/rustc_builtin_macros/src/eii.rs | 54 ++++++----- .../src/global_allocator.rs | 2 +- compiler/rustc_builtin_macros/src/offload.rs | 4 +- .../rustc_builtin_macros/src/test_harness.rs | 2 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 90 +++++++++---------- compiler/rustc_expand/src/build.rs | 2 +- compiler/rustc_expand/src/expand.rs | 4 +- .../rustc_hir/src/attrs/data_structures.rs | 2 +- .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 +- .../src/check/compare_eii.rs | 3 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 16 ++-- compiler/rustc_hir_analysis/src/lib.rs | 1 - compiler/rustc_metadata/src/eii.rs | 14 +-- compiler/rustc_parse/src/parser/item.rs | 17 ++-- compiler/rustc_passes/src/check_attr.rs | 85 +++++++++--------- compiler/rustc_resolve/src/def_collector.rs | 2 +- compiler/rustc_resolve/src/late.rs | 11 +-- .../clippy/clippy_utils/src/ast_utils/mod.rs | 20 ++--- tests/ui/eii/duplicate/both_decl_and_impl.rs | 27 ++++++ .../eii/duplicate/both_decl_and_impl.stderr | 28 ++++++ tests/ui/eii/duplicate/multiple_impls.rs | 20 ++++- .../eii/duplicate/multiple_impls.run.stdout | 2 - tests/ui/eii/duplicate/multiple_impls.stderr | 30 +++++++ tests/ui/eii/static/multiple_impls.rs | 20 ----- tests/ui/eii/static/multiple_impls.run.stdout | 1 - tests/ui/eii/static/multiple_impls.stderr | 10 --- 35 files changed, 320 insertions(+), 259 deletions(-) create mode 100644 tests/ui/eii/duplicate/both_decl_and_impl.rs create mode 100644 tests/ui/eii/duplicate/both_decl_and_impl.stderr delete mode 100644 tests/ui/eii/duplicate/multiple_impls.run.stdout create mode 100644 tests/ui/eii/duplicate/multiple_impls.stderr delete mode 100644 tests/ui/eii/static/multiple_impls.rs delete mode 100644 tests/ui/eii/static/multiple_impls.run.stdout delete mode 100644 tests/ui/eii/static/multiple_impls.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 1f50fd8ac36e0..110c64c103acb 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3981,7 +3981,7 @@ pub struct Fn { /// This function is an implementation of an externally implementable item (EII). /// This means, there was an EII declared somewhere and this function is the /// implementation that should be run when the declaration is called. - pub eii_impls: ThinVec, + pub eii_impl: Option>, } impl Fn { @@ -4073,9 +4073,7 @@ pub struct StaticItem { /// 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, + pub eii_impl: Option>, } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a768935f38fc3..9d4c32825e1e4 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -933,12 +933,12 @@ macro_rules! common_visitor_and_walkers { _ctxt, // Visibility is visited as a part of the item. _vis, - Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impls }, + Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impl }, ) => { let FnSig { header, decl, span } = sig; visit_visitable!($($mut)? vis, defaultness, ident, header, generics, decl, - contract, body, span, define_opaque, eii_impls + contract, body, span, define_opaque, eii_impl ); } FnKind::Closure(binder, coroutine_kind, decl, body) => diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 34c7137b8676d..3cc27be600965 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -170,15 +170,13 @@ impl<'hir> LoweringContext<'_, 'hir> { i: &ItemKind, ) -> Vec { match i { - ItemKind::Fn(Fn { eii_impls, .. }) | ItemKind::Static(StaticItem { eii_impls, .. }) - if eii_impls.is_empty() => - { - Vec::new() - } - ItemKind::Fn(Fn { eii_impls, .. }) | ItemKind::Static(StaticItem { eii_impls, .. }) => { - vec![hir::Attribute::Parsed(AttributeKind::EiiImpls( - eii_impls.iter().map(|i| self.lower_eii_impl(i)).collect(), - ))] + ItemKind::Fn(Fn { eii_impl: None, .. }) + | ItemKind::Static(StaticItem { eii_impl: None, .. }) => Vec::new(), + ItemKind::Fn(Fn { eii_impl: Some(eii_impl), .. }) + | ItemKind::Static(StaticItem { eii_impl: Some(eii_impl), .. }) => { + vec![hir::Attribute::Parsed(AttributeKind::EiiImpl(Box::new( + self.lower_eii_impl(eii_impl), + )))] } ItemKind::MacroDef(name, MacroDef { eii_declaration: Some(target), .. }) => self .lower_eii_decl(id, *name, target) @@ -226,7 +224,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind, vis_span, span: self.lower_span(i.span), - eii: find_attr!(attrs, EiiImpls(..) | EiiDeclaration(..)), + eii: find_attr!(attrs, EiiImpl(..) | EiiDeclaration(..)), }; self.arena.alloc(item) } @@ -259,7 +257,7 @@ impl<'hir> LoweringContext<'_, 'hir> { mutability: m, expr: e, define_opaque, - eii_impls: _, + eii_impl: _, }) => { let ident = self.lower_ident(*ident); let ty = self @@ -696,7 +694,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind, vis_span, span: this.lower_span(use_tree.span()), - eii: find_attr!(attrs, EiiImpls(..) | EiiDeclaration(..)), + eii: find_attr!(attrs, EiiImpl(..) | EiiDeclaration(..)), }; hir::OwnerNode::Item(this.arena.alloc(item)) }); @@ -763,7 +761,7 @@ impl<'hir> LoweringContext<'_, 'hir> { expr: _, safety, define_opaque, - eii_impls: _, + eii_impl: _, }) => { let ty = self .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 12a345aaeaf1d..62134046746c0 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1259,10 +1259,10 @@ impl<'a> AstValidator<'a> { } // Check EII implementation attributes against an allowlist. - fn check_eii_impl_attrs(&self, attrs: &[Attribute], eii_impls: &[EiiImpl]) { - if eii_impls.is_empty() { + fn check_eii_impl_attrs(&self, attrs: &[Attribute], eii_impl: &Option>) { + let Some(eii_impl) = eii_impl else { return; - } + }; let allowed_attrs: &[Symbol] = &[ sym::allow, @@ -1289,14 +1289,12 @@ impl<'a> AstValidator<'a> { } let attr_name = pprust::path_to_string(&normal.item.path); - for eii_impl in eii_impls { - self.dcx().emit_err(diagnostics::EiiImplAttributeNotSupported { - attr_span: attr.span, - attr_name: &attr_name, - eii_span: eii_impl.span, - eii_name: pprust::path_to_string(&eii_impl.eii_macro_path), - }); - } + self.dcx().emit_err(diagnostics::EiiImplAttributeNotSupported { + attr_span: attr.span, + attr_name: &attr_name, + eii_span: eii_impl.span, + eii_name: pprust::path_to_string(&eii_impl.eii_macro_path), + }); } } } @@ -1479,16 +1477,16 @@ impl Visitor<'_> for AstValidator<'_> { contract: _, body, define_opaque: _, - eii_impls, + eii_impl, }, ) => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); - for EiiImpl { eii_macro_path, .. } in eii_impls { + if let Some(EiiImpl { eii_macro_path, .. }) = eii_impl { self.visit_path(eii_macro_path); } - self.check_eii_impl_attrs(&item.attrs, eii_impls); + self.check_eii_impl_attrs(&item.attrs, eii_impl); let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)); if body.is_none() && !is_intrinsic && !self.is_sdylib_interface { @@ -1664,9 +1662,9 @@ impl Visitor<'_> for AstValidator<'_> { visit::walk_item(self, item); } - ItemKind::Static(StaticItem { expr, safety, eii_impls, .. }) => { + ItemKind::Static(StaticItem { expr, safety, eii_impl, .. }) => { self.check_item_safety(item.span, *safety); - self.check_eii_impl_attrs(&item.attrs, eii_impls); + self.check_eii_impl_attrs(&item.attrs, eii_impl); if matches!(safety, Safety::Unsafe(_)) { self.dcx().emit_err(diagnostics::UnsafeStatic { span: item.span }); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 1fb71b7b06299..04f78ea7f467a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -42,7 +42,7 @@ impl<'a> State<'a> { expr, safety, define_opaque, - eii_impls, + eii_impl, }) => self.print_item_const( *ident, Some(*mutability), @@ -53,7 +53,7 @@ impl<'a> State<'a> { *safety, ast::Defaultness::Implicit, define_opaque.as_deref(), - eii_impls, + eii_impl.as_deref(), ), ast::ForeignItemKind::TyAlias(ast::TyAlias { defaultness, @@ -94,10 +94,10 @@ impl<'a> State<'a> { safety: ast::Safety, defaultness: ast::Defaultness, define_opaque: Option<&[(ast::NodeId, ast::Path)]>, - eii_impls: &[EiiImpl], + eii_impl: Option<&EiiImpl>, ) { self.print_define_opaques(define_opaque); - for eii_impl in eii_impls { + if let Some(eii_impl) = eii_impl { self.print_eii_impl(eii_impl); } let (cb, ib) = self.head(""); @@ -196,7 +196,7 @@ impl<'a> State<'a> { mutability: mutbl, expr: body, define_opaque, - eii_impls, + eii_impl, }) => { self.print_safety(*safety); self.print_item_const( @@ -209,7 +209,7 @@ impl<'a> State<'a> { ast::Safety::Default, ast::Defaultness::Implicit, define_opaque.as_deref(), - eii_impls, + eii_impl.as_deref(), ); } ast::ItemKind::ConstBlock(ast::ConstBlockItem { id: _, span: _, block }) => { @@ -242,7 +242,7 @@ impl<'a> State<'a> { ast::Safety::Default, *defaultness, define_opaque.as_deref(), - &[], + None, ); } ast::ItemKind::Fn(func) => { @@ -631,7 +631,7 @@ impl<'a> State<'a> { ast::Safety::Default, *defaultness, define_opaque.as_deref(), - &[], + None, ); } ast::AssocItemKind::Type(ast::TyAlias { @@ -731,12 +731,12 @@ impl<'a> State<'a> { } fn print_fn_full(&mut self, vis: &ast::Visibility, attrs: &[ast::Attribute], func: &ast::Fn) { - let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impls } = + let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impl } = func; self.print_define_opaques(define_opaque.as_deref()); - for eii_impl in eii_impls { + if let Some(eii_impl) = eii_impl { self.print_eii_impl(eii_impl); } diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index d50fc78b51e14..57e589ac5a1e8 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -96,7 +96,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span contract: None, body, define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })); let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)]; diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 51ab44d8a03ef..0618c28759a66 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -344,7 +344,7 @@ mod llvm_enzyme { contract: None, body: Some(d_body), define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, }); let mut rustc_ad_attr = Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff))); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index cc036fab83c9d..03ccdbb902a96 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1083,7 +1083,7 @@ impl<'a> MethodDef<'a> { contract: None, body: Some(body_block), define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })), tokens: None, }) diff --git a/compiler/rustc_builtin_macros/src/diagnostics.rs b/compiler/rustc_builtin_macros/src/diagnostics.rs index 71dc17b108a97..ce5fb4e86dab6 100644 --- a/compiler/rustc_builtin_macros/src/diagnostics.rs +++ b/compiler/rustc_builtin_macros/src/diagnostics.rs @@ -1094,6 +1094,13 @@ pub(crate) struct CfgSelectNoMatches { pub span: Span, } +#[derive(Diagnostic)] +#[diag("a single item cannot both declare and implement EIIs")] +pub(crate) struct EiiBothDeclAndImpl { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`#[eii_declaration(...)]` is only valid on macros")] pub(crate) struct EiiExternTargetExpectedMacro { @@ -1117,21 +1124,18 @@ pub(crate) struct EiiExternTargetExpectedUnsafe { } #[derive(Diagnostic)] -#[diag("`#[{$name}]` is only valid on functions and statics")] -pub(crate) struct EiiSharedMacroTarget { +#[diag("a single item cannot implement multiple EIIs")] +pub(crate) struct EiiMultipleImplementations { #[primary_span] pub span: Span, - 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 { +#[diag("`#[{$name}]` is only valid on functions and statics")] +pub(crate) struct EiiSharedMacroTarget { #[primary_span] pub span: Span, + pub name: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 5a28416900372..cf50460422f52 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -10,10 +10,10 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::diagnostics::{ - EiiAttributeNotSupported, EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, - EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, - EiiSharedMacroInStatementPosition, EiiSharedMacroTarget, EiiStaticArgumentRequired, - EiiStaticDefaultApple, EiiStaticMultipleImplementations, EiiStaticMutable, + EiiAttributeNotSupported, EiiBothDeclAndImpl, EiiExternTargetExpectedList, + EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, + EiiMultipleImplementations, EiiOnlyOnce, EiiSharedMacroInStatementPosition, + EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefaultApple, EiiStaticMutable, }; /// ```rust @@ -125,6 +125,22 @@ fn eii_( } }; + match kind { + ItemKind::Fn(func) => { + if func.eii_impl.is_some() { + ecx.dcx().emit_err(EiiBothDeclAndImpl { span: eii_attr_span }); + return vec![Annotatable::Item(item)]; + } + } + ItemKind::Static(stat) => { + if stat.eii_impl.is_some() { + ecx.dcx().emit_err(EiiBothDeclAndImpl { span: eii_attr_span }); + return vec![Annotatable::Item(item)]; + } + } + _ => unreachable!("Target was checked earlier"), + }; + // only clone what we need let attrs = attrs.clone(); let vis = vis.clone(); @@ -298,7 +314,7 @@ fn generate_default_impl( _ => unreachable!("Target was checked earlier"), }; - let eii_impl = EiiImpl { + let eii_impl = Box::new(EiiImpl { node_id: DUMMY_NODE_ID, inner_span: macro_name.span, eii_macro_path: ast::Path::from_ident(macro_name), @@ -315,15 +331,17 @@ fn generate_default_impl( // NOTE: this is why EIIs can't be used on statements vec![Ident::from_str_and_span("self", foreign_item_name.span), foreign_item_name], )), - }; + }); let mut item_kind = item_kind.clone(); match &mut item_kind { ItemKind::Fn(func) => { - func.eii_impls.push(eii_impl); + assert!(func.eii_impl.is_none()); + func.eii_impl = Some(eii_impl); } ItemKind::Static(stat) => { - stat.eii_impls.push(eii_impl); + assert!(stat.eii_impl.is_none()); + stat.eii_impl = Some(eii_impl); } _ => unreachable!("Target was checked earlier"), }; @@ -579,16 +597,9 @@ pub(crate) fn eii_shared_macro( return vec![item]; }; - let eii_impls = match &mut i.kind { - ItemKind::Fn(func) => &mut func.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 - } + let eii_impl = match &mut i.kind { + ItemKind::Fn(func) => &mut func.eii_impl, + ItemKind::Static(stat) => &mut stat.eii_impl, _ => { ecx.dcx() .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) }); @@ -611,7 +622,10 @@ pub(crate) fn eii_shared_macro( return vec![item]; }; - eii_impls.push(EiiImpl { + if eii_impl.is_some() { + ecx.dcx().emit_err(EiiMultipleImplementations { span }); + } + *eii_impl = Some(Box::new(EiiImpl { node_id: DUMMY_NODE_ID, inner_span: meta_item.path.span, eii_macro_path: meta_item.path.clone(), @@ -619,7 +633,7 @@ pub(crate) fn eii_shared_macro( span, is_default, known_eii_macro_resolution: None, - }); + })); vec![item] } diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 72b493e313326..00ed0f52d6a6f 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -97,7 +97,7 @@ impl AllocFnFactory<'_, '_> { contract: None, body, define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })); let item = self.cx.item(self.span, self.attrs(method), kind); self.cx.stmt_item(self.ty_span, item) diff --git a/compiler/rustc_builtin_macros/src/offload.rs b/compiler/rustc_builtin_macros/src/offload.rs index cdb3ba22ec6c8..e47ccc0d85f7d 100644 --- a/compiler/rustc_builtin_macros/src/offload.rs +++ b/compiler/rustc_builtin_macros/src/offload.rs @@ -85,7 +85,7 @@ pub(crate) fn expand_kernel( contract: None, body, define_opaque: None, - eii_impls: Default::default(), + eii_impl: None, }); let extern_gpu_kernel = ast::Extern::from_abi( @@ -157,7 +157,7 @@ pub(crate) fn expand_kernel( contract: None, body: Some(body), define_opaque: None, - eii_impls: Default::default(), + eii_impl: None, }); for param in host_fn.sig.decl.inputs.iter_mut() { diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index ff9d9f10dd4e1..5d20fed223468 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -347,7 +347,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { contract: None, body: Some(main_body), define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })); let main = Box::new(ast::Item { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 0389aa56bafd6..3e24b62125fea 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -219,32 +219,31 @@ fn process_builtin_attrs( AttributeKind::RustcEiiForeignItem => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; } - AttributeKind::EiiImpls(impls) => { - for i in impls { - let foreign_item = match i.resolution { - EiiImplResolution::Macro(def_id) => { - let Some(extern_item) = find_attr!(tcx, def_id, EiiDeclaration(target) => target.foreign_item - ) else { - tcx.dcx().span_delayed_bug( - i.span, - "resolved to something that's not an EII", - ); - continue; - }; - extern_item - } - EiiImplResolution::Known(def_id) => def_id, - EiiImplResolution::Error(_eg) => continue, - }; + AttributeKind::EiiImpl(i) => { + let foreign_item = match i.resolution { + EiiImplResolution::Macro(def_id) => { + let Some(extern_item) = find_attr!(tcx, def_id, EiiDeclaration(target) => target.foreign_item + ) else { + tcx.dcx().span_delayed_bug( + i.span, + "resolved to something that's not an EII", + ); + continue; + }; + extern_item + } + EiiImplResolution::Known(def_id) => def_id, + EiiImplResolution::Error(_eg) => continue, + }; - // this is to prevent a bug where a single crate defines both the default and explicit implementation - // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure - // what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent. - // However, the fact that the default one of has weak linkage isn't considered and you sometimes get that - // the default implementation is used while an explicit implementation is given. - if - // if this is a default impl - i.is_default + // this is to prevent a bug where a single crate defines both the default and explicit implementation + // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure + // what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent. + // However, the fact that the default one of has weak linkage isn't considered and you sometimes get that + // the default implementation is used while an explicit implementation is given. + if + // if this is a default impl + i.is_default // iterate over all implementations *in the current crate* // (this is ok since we generate codegen fn attrs in the local crate) // if any of them is *not default* then don't emit the alias. @@ -252,28 +251,27 @@ fn process_builtin_attrs( let (_, impls) = tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).unwrap_or_else(|| bug!("EII impl should have an entry")); impls.iter().any(|(_, imp)| !imp.is_default) } - { - continue; - } + { + continue; + } - codegen_fn_attrs.foreign_item_symbol_aliases.push(( - foreign_item, - if i.is_default { Linkage::WeakAny } else { Linkage::External }, - Visibility::Default, - )); - codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; - - // If the declaration is `#[track_caller]`, derive it onto the implementation - // too. The shim that forwards to this impl (see `add_function_aliases`) takes - // its ABI from the impl's `fn_abi`, so every impl must agree on whether the - // caller-location argument is present, otherwise it would be silently dropped. - if tcx - .codegen_fn_attrs(foreign_item) - .flags - .contains(CodegenFnAttrFlags::TRACK_CALLER) - { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; - } + codegen_fn_attrs.foreign_item_symbol_aliases.push(( + foreign_item, + if i.is_default { Linkage::WeakAny } else { Linkage::External }, + Visibility::Default, + )); + codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; + + // If the declaration is `#[track_caller]`, derive it onto the implementation + // too. The shim that forwards to this impl (see `add_function_aliases`) takes + // its ABI from the impl's `fn_abi`, so every impl must agree on whether the + // caller-location argument is present, otherwise it would be silently dropped. + if tcx + .codegen_fn_attrs(foreign_item) + .flags + .contains(CodegenFnAttrFlags::TRACK_CALLER) + { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; } } AttributeKind::ThreadLocal => { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index b87dfd0198efc..4a8541ed4c6b7 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -707,7 +707,7 @@ impl<'a> ExtCtxt<'a> { mutability, expr: Some(expr), define_opaque: None, - eii_impls: Default::default(), + eii_impl: None, } .into(), ), diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 187dcea4f91a5..045233c0c4d21 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -804,7 +804,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { None, ) } - // When a function has EII implementations attached (via `eii_impls`), + // When a function has EII implementations attached (via `eii_impl`), // use fake tokens so the pretty-printer re-emits the EII attribute // (e.g. `#[hello]`) in the token stream. Without this, the EII // attribute is lost during the token roundtrip performed by @@ -812,7 +812,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // breaking the EII link on the resulting re-parsed item. Annotatable::Item(item_inner) if matches!(&item_inner.kind, - ItemKind::Fn(f) if !f.eii_impls.is_empty()) => + ItemKind::Fn(f) if f.eii_impl.is_some()) => { rustc_parse::fake_token_stream_for_item( &self.cx.sess.psess, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 165f06d2fde8b..78a15eeb923a5 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1076,7 +1076,7 @@ pub enum AttributeKind { EiiDeclaration(EiiDecl), /// Implementation detail of `#[eii]` - EiiImpls(ThinVec), + EiiImpl(Box), /// Represents [`#[export_name]`](https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute). ExportName { diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index dded70ccd08ef..a5a1fc2482b4e 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -40,7 +40,7 @@ impl AttributeKind { Doc(_) => Yes, DocComment { .. } => Yes, EiiDeclaration(_) => Yes, - EiiImpls(..) => No, + EiiImpl(..) => No, ExportName { .. } => Yes, ExportStable => No, Feature(..) => No, diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index 57824a91a680f..d9fc3bbcf08c2 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -301,8 +301,7 @@ fn check_no_generics<'tcx>( // since in that case it looks like a duplicate error: the declaration of the EII already can't contain generics. // So, we check here if at least one of the eii impls has ImplResolution::Macro, which indicates it's // not generated as part of the declaration. - && find_attr!(tcx, external_impl, EiiImpls(impls) if impls.iter().any(|i| matches!(i.resolution, EiiImplResolution::Macro(_))) - ) + && find_attr!(tcx, external_impl, EiiImpl(i) if matches!(i.resolution, EiiImplResolution::Macro(_))) { tcx.dcx().emit_err(EiiWithGenerics { span: tcx.def_span(external_impl), diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 35628e54769b4..caf64fd6894f7 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1153,9 +1153,7 @@ fn check_item_fn( 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 - find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_flat_iter() - { + if let Some(EiiImpl { resolution, span, .. }) = find_attr!(tcx, def_id, EiiImpl(i) => &**i) { let (foreign_item, name) = match resolution { EiiImplResolution::Macro(def_id) => { // we expect this macro to have the `EiiMacroFor` attribute, that points to a function @@ -1166,11 +1164,11 @@ fn check_eiis_fn(tcx: TyCtxt<'_>, def_id: LocalDefId) { (foreign_item, tcx.item_name(*def_id)) } else { tcx.dcx().span_delayed_bug(*span, "resolved to something that's not an EII"); - continue; + return; } } EiiImplResolution::Known(def_id) => (*def_id, tcx.item_name(*def_id)), - EiiImplResolution::Error(_eg) => continue, + EiiImplResolution::Error(_eg) => return, }; let _ = compare_eii_function_types(tcx, def_id, foreign_item, name, *span); @@ -1180,9 +1178,7 @@ fn check_eiis_fn(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_flat_iter() - { + if let Some(EiiImpl { resolution, span, .. }) = find_attr!(tcx, def_id, EiiImpl(i) => &**i) { let (foreign_item, name) = match resolution { EiiImplResolution::Macro(def_id) => { // we expect this macro to have the `EiiMacroFor` attribute, that points to a function @@ -1193,11 +1189,11 @@ fn check_eiis_static<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, ty: Ty<'tcx>) (foreign_item, tcx.item_name(*def_id)) } else { tcx.dcx().span_delayed_bug(*span, "resolved to something that's not an EII"); - continue; + return; } } EiiImplResolution::Known(def_id) => (*def_id, tcx.item_name(*def_id)), - EiiImplResolution::Error(_eg) => continue, + EiiImplResolution::Error(_eg) => return, }; let _ = compare_eii_statics(tcx, def_id, ty, foreign_item, name, *span); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 1473b0d108fb3..ebbf63b947a93 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -60,7 +60,6 @@ This API is completely unstable and subject to change. #![feature(gen_blocks)] #![feature(iter_intersperse)] #![feature(never_type)] -#![feature(option_into_flat_iter)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] #![feature(unwrap_infallible)] diff --git a/compiler/rustc_metadata/src/eii.rs b/compiler/rustc_metadata/src/eii.rs index 4328e8de901d8..da6d9e85bc4fa 100644 --- a/compiler/rustc_metadata/src/eii.rs +++ b/compiler/rustc_metadata/src/eii.rs @@ -35,7 +35,12 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap // iterate over all items in the current crate for id in tcx.hir_crate_items(()).eiis() { - for i in find_attr!(tcx, id, EiiImpls(e) => e).into_flat_iter() { + // if we find a new declaration, add it to the list without a known implementation + if let Some(decl) = find_attr!(tcx, id, EiiDeclaration(d) => *d) { + eiis.entry(decl.foreign_item).or_insert((decl, Default::default())); + } + + if let Some(i) = find_attr!(tcx, id, EiiImpl(i) => i) { let (foreign_item, decl) = match i.resolution { EiiImplResolution::Macro(macro_defid) => { // find the decl for this one if it wasn't in yet (maybe it's from the local crate? not very useful but not illegal) @@ -63,12 +68,7 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap eiis.entry(foreign_item) .or_insert_with(|| (decl, Default::default())) .1 - .insert(id.into(), *i); - } - - // if we find a new declaration, add it to the list without a known implementation - if let Some(decl) = find_attr!(tcx, id, EiiDeclaration(d) => *d) { - eiis.entry(decl.foreign_item).or_insert((decl, Default::default())); + .insert(id.into(), **i); } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 0fa592459167a..1306f1fcfb1ce 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -283,7 +283,7 @@ impl<'a> Parser<'a> { contract, body, define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })) } else if self.eat_keyword_case(exp!(Extern), case) { if self.eat_keyword_case(exp!(Crate), case) { @@ -1257,7 +1257,7 @@ impl<'a> Parser<'a> { mutability: _, expr, define_opaque, - eii_impls: _, + eii_impl: _, }) => { self.dcx() .emit_err(diagnostics::AssociatedStaticItemNotAllowed { span }); @@ -1523,7 +1523,7 @@ impl<'a> Parser<'a> { expr: body, safety: Safety::Default, define_opaque: None, - eii_impls: ThinVec::default(), + eii_impl: None, })) } _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), @@ -1661,15 +1661,8 @@ impl<'a> Parser<'a> { self.expect_semi()?; - let item = StaticItem { - ident, - ty, - safety, - mutability, - expr, - define_opaque: None, - eii_impls: ThinVec::default(), - }; + let item = + StaticItem { ident, ty, safety, mutability, expr, define_opaque: None, eii_impl: None }; Ok(ItemKind::Static(Box::new(item))) } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6489167afcdac..d1d82b2ed43e0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -211,7 +211,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_rustc_legacy_const_generics(item, *attr_span, fn_indexes) } AttributeKind::Doc(attr) => self.check_doc_attrs(attr, hir_id, target), - AttributeKind::EiiImpls(impls) => self.check_eii_impl(impls), + AttributeKind::EiiImpl(eii_impl) => self.check_eii_impl(eii_impl), AttributeKind::RustcMustImplementOneOf { attr_span, fn_names } => { self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id, target) } @@ -474,51 +474,50 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that each externally implementable item (EII) implementation uses `unsafe` /// exactly when its declaration requires it. - fn check_eii_impl(&self, impls: &[EiiImpl]) { - for EiiImpl { span, inner_span, resolution, impl_unsafe_span, is_default: _ } in impls { - let impl_unsafe = match resolution { - EiiImplResolution::Macro(eii_macro) => find_attr!( - self.tcx, - *eii_macro, - EiiDeclaration(EiiDecl { impl_unsafe, .. }) => *impl_unsafe - ), - EiiImplResolution::Known(foreign_item_did) => self - .tcx - .externally_implementable_items(foreign_item_did.krate) - .get(foreign_item_did) - .map(|(decl, _)| decl.impl_unsafe), - EiiImplResolution::Error(_) => None, - }; - let Some(needs_unsafe) = impl_unsafe else { - continue; - }; + fn check_eii_impl(&self, eii_impl: &EiiImpl) { + let EiiImpl { span, inner_span, resolution, impl_unsafe_span, is_default: _ } = eii_impl; + let impl_unsafe = match resolution { + EiiImplResolution::Macro(eii_macro) => find_attr!( + self.tcx, + *eii_macro, + EiiDeclaration(EiiDecl { impl_unsafe, .. }) => *impl_unsafe + ), + EiiImplResolution::Known(foreign_item_did) => self + .tcx + .externally_implementable_items(foreign_item_did.krate) + .get(foreign_item_did) + .map(|(decl, _)| decl.impl_unsafe), + EiiImplResolution::Error(_) => None, + }; + let Some(needs_unsafe) = impl_unsafe else { + return; + }; - let name = match resolution { - EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), - EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), - EiiImplResolution::Error(_) => unreachable!(), - }; + let name = match resolution { + EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), + EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), + EiiImplResolution::Error(_) => unreachable!(), + }; - match (needs_unsafe, *impl_unsafe_span) { - (true, None) => { - self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { - span: *span, - name, - suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { - left: inner_span.shrink_to_lo(), - right: inner_span.shrink_to_hi(), - }, - }); - } - (false, Some(unsafe_span)) => { - self.dcx().emit_err(diagnostics::EiiImplCannotBeUnsafe { - impl_span: *span, - unsafe_span, - name, - }); - } - _ => {} + match (needs_unsafe, *impl_unsafe_span) { + (true, None) => { + self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { + span: *span, + name, + suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { + left: inner_span.shrink_to_lo(), + right: inner_span.shrink_to_hi(), + }, + }); + } + (false, Some(unsafe_span)) => { + self.dcx().emit_err(diagnostics::EiiImplCannotBeUnsafe { + impl_span: *span, + unsafe_span, + name, + }); } + _ => {} } } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 63bff7a3f4498..97ca994c37540 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -303,7 +303,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { expr: _, safety, define_opaque: _, - eii_impls: _, + eii_impl: _, }) => { let safety = match safety { ast::Safety::Unsafe(_) | ast::Safety::Default => hir::Safety::Unsafe, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f30e6844c861c..fc723586c1acc 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1147,7 +1147,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc debug!("(resolving function) entering function"); if let FnKind::Fn(_, _, f) = fn_kind { - self.resolve_eii(&f.eii_impls); + self.resolve_eii(f.eii_impl.as_deref()); } // Create a value rib for the function. @@ -2940,7 +2940,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } ItemKind::Static(ast::StaticItem { - ident, ty, expr, define_opaque, eii_impls, .. + ident, ty, expr, define_opaque, eii_impl, .. }) => { self.with_static_rib(def_kind, |this| { this.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Static), |this| { @@ -2953,7 +2953,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } }); self.resolve_define_opaques(define_opaque); - self.resolve_eii(&eii_impls); + self.resolve_eii(eii_impl.as_deref()); } ItemKind::Const(ast::ConstItem { @@ -5568,8 +5568,9 @@ 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 { + fn resolve_eii(&mut self, eii_impl: Option<&EiiImpl>) { + if let Some(EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. }) = eii_impl + { // 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 { 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 3f0b99aa4d780..523e799ef2522 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -332,7 +332,7 @@ fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { expr: le, safety: ls, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Static(box StaticItem { ident: ri, @@ -341,7 +341,7 @@ fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { expr: re, safety: rs, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref()), ( @@ -381,7 +381,7 @@ fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { contract: lc, body: lb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Fn(box ast::Fn { defaultness: rd, @@ -391,7 +391,7 @@ fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { contract: rc, body: rb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => { eq_defaultness(*ld, *rd) @@ -539,7 +539,7 @@ fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { expr: le, safety: ls, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Static(box StaticItem { ident: ri, @@ -548,7 +548,7 @@ fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { expr: re, safety: rs, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_deref(), re.as_deref()) && ls == rs, ( @@ -560,7 +560,7 @@ fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { contract: lc, body: lb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Fn(box ast::Fn { defaultness: rd, @@ -570,7 +570,7 @@ fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { contract: rc, body: rb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => { eq_defaultness(*ld, *rd) @@ -649,7 +649,7 @@ fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { contract: lc, body: lb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Fn(box ast::Fn { defaultness: rd, @@ -659,7 +659,7 @@ fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { contract: rc, body: rb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => { eq_defaultness(*ld, *rd) diff --git a/tests/ui/eii/duplicate/both_decl_and_impl.rs b/tests/ui/eii/duplicate/both_decl_and_impl.rs new file mode 100644 index 0000000000000..a2fc571d3f497 --- /dev/null +++ b/tests/ui/eii/duplicate/both_decl_and_impl.rs @@ -0,0 +1,27 @@ +//@ ignore-backends: gcc +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu +// Tests that one item can't both define and impl an EII at the same time +#![feature(extern_item_impls)] + +#[eii] +fn a(x: u64); + +#[a] +#[eii] +//~^ ERROR a single item cannot both declare and implement EIIs +fn b(x: u64) {} + +#[eii] +fn c(x: u64); +//~^ ERROR `#[c]` function required, but not found + +#[eii] +#[c] +fn d(x: u64) {} +//~^ ERROR only a small subset of attributes are supported on externally implementable items + +fn main() { + a(42); + b(42); +} diff --git a/tests/ui/eii/duplicate/both_decl_and_impl.stderr b/tests/ui/eii/duplicate/both_decl_and_impl.stderr new file mode 100644 index 0000000000000..1cec485a90cff --- /dev/null +++ b/tests/ui/eii/duplicate/both_decl_and_impl.stderr @@ -0,0 +1,28 @@ +error: a single item cannot both declare and implement EIIs + --> $DIR/both_decl_and_impl.rs:11:1 + | +LL | #[eii] + | ^^^^^^ + +error: only a small subset of attributes are supported on externally implementable items + --> $DIR/both_decl_and_impl.rs:21:1 + | +LL | fn d(x: u64) {} + | ^^^^^^^^^^^^ + | +note: this attribute is not supported + --> $DIR/both_decl_and_impl.rs:20:1 + | +LL | #[c] + | ^^^^ + +error: `#[c]` function required, but not found + --> $DIR/both_decl_and_impl.rs:16:4 + | +LL | fn c(x: u64); + | ^ expected because `#[c]` was declared here in crate `both_decl_and_impl` + | + = help: expected at least one implementation in crate `both_decl_and_impl` or any of its dependencies + +error: aborting due to 3 previous errors + diff --git a/tests/ui/eii/duplicate/multiple_impls.rs b/tests/ui/eii/duplicate/multiple_impls.rs index 80f6147789743..3e541cb16b131 100644 --- a/tests/ui/eii/duplicate/multiple_impls.rs +++ b/tests/ui/eii/duplicate/multiple_impls.rs @@ -1,25 +1,37 @@ -//@ run-pass -//@ check-run-results //@ ignore-backends: gcc // FIXME(#125418): linking on Windows GNU targets is not yet supported. //@ ignore-windows-gnu -// Tests whether one function could implement two EIIs. +// Tests that one item can't implement two EIIs #![feature(extern_item_impls)] #[eii] fn a(x: u64); +//~^ ERROR `#[a]` function required, but not found #[eii] fn b(x: u64); #[a] #[b] +//~^ ERROR a single item cannot implement multiple EIIs fn implementation(x: u64) { println!("{x:?}") } -// what you would write: +#[eii(c)] +//~^ ERROR `#[c]` static required, but not found +static C: u64; + +#[eii(d)] +static D: u64; + +#[c] +#[d] +//~^ ERROR a single item cannot implement multiple EIIs +static IMPL: u64 = 5; + fn main() { a(42); b(42); + println!("{C} {D} {IMPL}") } diff --git a/tests/ui/eii/duplicate/multiple_impls.run.stdout b/tests/ui/eii/duplicate/multiple_impls.run.stdout deleted file mode 100644 index daaac9e303029..0000000000000 --- a/tests/ui/eii/duplicate/multiple_impls.run.stdout +++ /dev/null @@ -1,2 +0,0 @@ -42 -42 diff --git a/tests/ui/eii/duplicate/multiple_impls.stderr b/tests/ui/eii/duplicate/multiple_impls.stderr new file mode 100644 index 0000000000000..efeec635c859a --- /dev/null +++ b/tests/ui/eii/duplicate/multiple_impls.stderr @@ -0,0 +1,30 @@ +error: a single item cannot implement multiple EIIs + --> $DIR/multiple_impls.rs:15:1 + | +LL | #[b] + | ^^^^ + +error: a single item cannot implement multiple EIIs + --> $DIR/multiple_impls.rs:29:1 + | +LL | #[d] + | ^^^^ + +error: `#[a]` function required, but not found + --> $DIR/multiple_impls.rs:8:4 + | +LL | fn a(x: u64); + | ^ expected because `#[a]` was declared here in crate `multiple_impls` + | + = help: expected at least one implementation in crate `multiple_impls` or any of its dependencies + +error: `#[c]` static required, but not found + --> $DIR/multiple_impls.rs:21:7 + | +LL | #[eii(c)] + | ^ expected because `#[c]` was declared here in crate `multiple_impls` + | + = help: expected at least one implementation in crate `multiple_impls` or any of its dependencies + +error: aborting due to 4 previous errors + diff --git a/tests/ui/eii/static/multiple_impls.rs b/tests/ui/eii/static/multiple_impls.rs deleted file mode 100644 index 1129417b958ca..0000000000000 --- a/tests/ui/eii/static/multiple_impls.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ ignore-backends: gcc -// FIXME(#125418): linking on Windows GNU targets is not yet supported. -//@ ignore-windows-gnu -// Tests whether one function could implement two EIIs. -#![feature(extern_item_impls)] - -#[eii(a)] -static A: u64; - -#[eii(b)] -static B: u64; - -#[a] -#[b] -//~^ ERROR static cannot implement multiple EIIs -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 deleted file mode 100644 index 58945c2b48291..0000000000000 --- a/tests/ui/eii/static/multiple_impls.run.stdout +++ /dev/null @@ -1 +0,0 @@ -5 5 5 diff --git a/tests/ui/eii/static/multiple_impls.stderr b/tests/ui/eii/static/multiple_impls.stderr deleted file mode 100644 index b31331f2483f1..0000000000000 --- a/tests/ui/eii/static/multiple_impls.stderr +++ /dev/null @@ -1,10 +0,0 @@ -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 -