From 5768b234de07a3d39bb35deb36d3af92b790bc75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 8 Dec 2025 11:46:59 +0100 Subject: [PATCH] use our own alternative to STD_INTERNAL_SYMBOL and make sure we mangle EIIs properly --- .../src/attributes/codegen_attrs.rs | 9 +++++++++ compiler/rustc_attr_parsing/src/context.rs | 5 +++-- compiler/rustc_builtin_macros/src/eii.rs | 20 ++++++++++++++++++- .../src/back/symbol_export.rs | 9 +++++++-- .../rustc_codegen_ssa/src/codegen_attrs.rs | 11 +++++++++- compiler/rustc_feature/src/builtin_attrs.rs | 5 +++++ .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../src/middle/codegen_fn_attrs.rs | 13 ++++++++++++ .../src/middle/exported_symbols.rs | 1 + compiler/rustc_monomorphize/src/collector.rs | 12 ++++++----- .../rustc_monomorphize/src/partitioning.rs | 12 +++++++++-- compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + tests/ui/eii/same-symbol.run.stdout | 2 +- 15 files changed, 91 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index b4ecbe6e4de6..c5800324fa99 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -699,3 +699,12 @@ impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisPa const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis; } + +pub(crate) struct EiiExternItemParser; + +impl NoArgsAttributeParser for EiiExternItemParser { + const PATH: &[Symbol] = &[sym::rustc_eii_extern_item]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiExternItem; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f41ea3708788..894b4c564ab7 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -20,8 +20,8 @@ use crate::attributes::allow_unstable::{ }; use crate::attributes::body::CoroutineParser; use crate::attributes::codegen_attrs::{ - ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, - NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, + ColdParser, CoverageParser, EiiExternItemParser, ExportNameParser, ForceTargetFeatureParser, + NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, }; @@ -227,6 +227,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index f1161c644ef5..b97cb1daec53 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -209,6 +209,24 @@ fn eii_( } // extern "…" { safe fn item(); } + let mut extern_item_attrs = attrs.clone(); + extern_item_attrs.push(ast::Attribute { + kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr { + item: ast::AttrItem { + unsafety: ast::Safety::Default, + // Add the rustc_eii_extern_item on the foreign item. Usually, foreign items are mangled. + // This attribute makes sure that we later know that this foreign item's symbol should not be. + path: ast::Path::from_ident(Ident::new(sym::rustc_eii_extern_item, span)), + args: ast::AttrArgs::Empty, + tokens: None, + }, + tokens: None, + })), + id: ecx.sess.psess.attr_id_generator.mk_attr_id(), + style: ast::AttrStyle::Outer, + span, + }); + let extern_block = Box::new(ast::Item { attrs: ast::AttrVec::default(), id: ast::DUMMY_NODE_ID, @@ -219,7 +237,7 @@ fn eii_( safety: ast::Safety::Unsafe(span), abi, items: From::from([Box::new(ast::ForeignItem { - attrs: attrs.clone(), + attrs: extern_item_attrs, id: ast::DUMMY_NODE_ID, span: item_span, vis, diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 9d06de2b35c2..27989f6f5ea2 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -127,7 +127,10 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap, sym_def_id: DefId) -> SymbolExportLevel let is_extern = codegen_fn_attrs.contains_extern_indicator(); let std_internal = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); + let eii = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM); - if is_extern && !std_internal { + if is_extern && !std_internal && !eii { let target = &tcx.sess.target.llvm_target; // WebAssembly cannot export data symbols, so reduce their export level + // FIXME(jdonszelmann) don't do a substring match here. if target.contains("emscripten") { if let DefKind::Static { .. } = tcx.def_kind(sym_def_id) { return SymbolExportLevel::Rust; diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 6aea249d6356..1933947ee0de 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -313,6 +313,9 @@ fn process_builtin_attrs( AttributeKind::ObjcSelector { methname, .. } => { codegen_fn_attrs.objc_selector = Some(*methname); } + AttributeKind::EiiExternItem => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; + } AttributeKind::EiiImpls(impls) => { for i in impls { let extern_item = find_attr!( @@ -344,7 +347,7 @@ fn process_builtin_attrs( if i.is_default { Linkage::LinkOnceAny } else { Linkage::External }, Visibility::Default, )); - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; } } _ => {} @@ -448,6 +451,12 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code // * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way // both for exports and imports through foreign items. This is handled further, // during symbol mangling logic. + } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM) + { + // * externally implementable items keep their mangled symbol name. + // multiple EIIs can have the same name, so not mangling them would be a bug. + // Implementing an EII does the appropriate name resolution to make sure the implementations + // get the same symbol name as the *mangled* foreign item they refer to so that's all good. } else if codegen_fn_attrs.symbol_name.is_some() { // * This can be overridden with the `#[link_name]` attribute } else { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 6eefb2f48d12..c70dbc15e552 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -961,6 +961,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ allow_internal_unsafe, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint", ), + gated!( + rustc_eii_extern_item, Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::Yes, eii_internals, + "used internally to mark types with a `transparent` representation when it is guaranteed by the documentation", + ), rustc_attr!( rustc_allowed_through_unstable_modules, Normal, template!(NameValueStr: "deprecation message"), WarnFollowing, EncodeCrossCrate::No, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 28433f261220..34d78afca9b2 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -709,6 +709,9 @@ pub enum AttributeKind { /// Represents `#[rustc_dummy]`. Dummy, + /// Implementation detail of `#[eii]` + EiiExternItem, + /// Implementation detail of `#[eii]` EiiExternTarget(EiiDecl), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index e42d511da695..4cb786f59bfe 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -43,6 +43,7 @@ impl AttributeKind { Doc(_) => Yes, DocComment { .. } => Yes, Dummy => No, + EiiExternItem => No, EiiExternTarget(_) => Yes, EiiImpls(..) => No, ExportName { .. } => Yes, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 7922cc38aaab..9033d9c46d54 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -43,6 +43,10 @@ impl<'tcx> TyCtxt<'tcx> { attrs.to_mut().flags.remove(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); } + if attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM) { + attrs.to_mut().flags.remove(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM); + } + if attrs.symbol_name.is_some() { attrs.to_mut().symbol_name = None; } @@ -199,6 +203,12 @@ bitflags::bitflags! { const FOREIGN_ITEM = 1 << 16; /// `#[rustc_offload_kernel]`: indicates that this is an offload kernel, an extra ptr arg will be added. const OFFLOAD_KERNEL = 1 << 17; + /// Externally implementable item symbols act a little like `RUSTC_STD_INTERNAL_SYMBOL`. + /// When a crate declares an EII and dependencies expect the symbol to exist, + /// they will refer to this symbol name before a definition is given. + /// As such, we must make sure these symbols really do exist in the final binary/library. + /// This flag is put on both the implementations of EIIs and the foreign item they implement. + const EXTERNALLY_IMPLEMENTABLE_ITEM = 1 << 18; } } rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags } @@ -242,6 +252,9 @@ impl CodegenFnAttrs { self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + // note: for these we do also set a symbol name so technically also handled by the + // condition below. However, I think that regardless these should be treated as extern. + || self.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM) || self.symbol_name.is_some() || match self.linkage { // These are private, so make sure we don't try to consider diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index c70ff398ceaf..58fc32078039 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -41,6 +41,7 @@ pub struct SymbolExportInfo { /// Was the symbol marked as `#[used(compiler)]` or `#[used(linker)]`? pub used: bool, /// Was the symbol marked as `#[rustc_std_internal_symbol]`? + /// We also use this for externally implementable items. pub rustc_std_internal_symbol: bool, } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 948f965ed7ad..cd699436e012 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1643,11 +1643,13 @@ impl<'v> RootCollector<'_, 'v> { MonoItemCollectionStrategy::Lazy => { self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id) || self.tcx.is_reachable_non_generic(def_id) - || self - .tcx - .codegen_fn_attrs(def_id) - .flags - .contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + || { + let flags = self.tcx.codegen_fn_attrs(def_id).flags; + flags.intersects( + CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL + | CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM, + ) + } } } } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index b87ab840a32d..1c8d6db08c31 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -872,7 +872,7 @@ fn mono_item_visibility<'tcx>( // visibility. In some situations though we'll want to prevent this // symbol from being internalized. // - // There's two categories of items here: + // There's three categories of items here: // // * First is weak lang items. These are basically mechanisms for // libcore to forward-reference symbols defined later in crates like @@ -902,8 +902,16 @@ fn mono_item_visibility<'tcx>( // visibility below. Like the weak lang items, though, we can't let // LLVM internalize them as this decision is left up to the linker to // omit them, so prevent them from being internalized. + // + // * Externally implementable items. They work (in this case) pretty much the same as + // RUSTC_STD_INTERNAL_SYMBOL in that their implementation is also chosen later in + // the compilation process and we can't let them be internalized and they can't + // show up as an external interface. let attrs = tcx.codegen_fn_attrs(def_id); - if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + if attrs.flags.intersects( + CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL + | CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM, + ) { *can_be_internalized = false; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d5d2d64ffaf7..34f73212e90b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -217,6 +217,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }, Attribute::Parsed( AttributeKind::EiiExternTarget { .. } + | AttributeKind::EiiExternItem | AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect | AttributeKind::MacroTransparency(_) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8f43a31ea4b1..8e464b55d6c2 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1944,6 +1944,7 @@ symbols! { rustc_dump_user_args, rustc_dump_vtable, rustc_effective_visibility, + rustc_eii_extern_item, rustc_evaluate_where_clauses, rustc_expected_cgu_reuse, rustc_force_inline, diff --git a/tests/ui/eii/same-symbol.run.stdout b/tests/ui/eii/same-symbol.run.stdout index 873ddc86a9d5..463021151d29 100644 --- a/tests/ui/eii/same-symbol.run.stdout +++ b/tests/ui/eii/same-symbol.run.stdout @@ -1,2 +1,2 @@ foo1 -foo1 +foo2