From 1fee4f72517908b03ef3a6b59d44d882fa77bee8 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 15 Jul 2026 15:38:23 +0200 Subject: [PATCH 1/6] Lower #[no_mangle] to #[symbol_name]/#[link_name] This reduces the amount of special casing #[no_mangle] needs in the rest of the compiler. --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 27 ++++++++++++------- .../src/middle/codegen_fn_attrs.rs | 10 +------ compiler/rustc_symbol_mangling/src/lib.rs | 5 ---- src/tools/miri/src/shims/foreign_items.rs | 1 - 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 3e24b62125fea..153dae08a4c03 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -93,8 +93,11 @@ fn process_builtin_attrs( AttributeKind::LinkSection { name } => codegen_fn_attrs.link_section = Some(*name), AttributeKind::NoMangle(attr_span) => { interesting_spans.no_mangle = Some(*attr_span); - if tcx.opt_item_name(did.to_def_id()).is_some() { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + if let Some(name) = tcx.opt_item_name(did.to_def_id()) { + // Don't override #[link_name] or #[export_name] + if codegen_fn_attrs.symbol_name.is_none() { + codegen_fn_attrs.symbol_name = Some(name); + } } else { tcx.dcx() .span_delayed_bug(*attr_span, "no_mangle should be on a named function"); @@ -410,7 +413,7 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code // import will *still* be mangled despite this. // // if none of the exceptions apply; apply no_mangle - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + codegen_fn_attrs.symbol_name = Some(tcx.item_name(did)); } } } @@ -543,22 +546,24 @@ fn handle_lang_items( // strippable by the linker. // // Additionally weak lang items have predetermined symbol names. - if let Some(lang_item) = lang_item + let link_name_override = if let Some(lang_item) = lang_item && let Some(link_name) = lang_item.link_name() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; - codegen_fn_attrs.symbol_name = Some(link_name); - } + Some(link_name) + } else { + None + }; - // error when using no_mangle on a lang item item + // error when using no_mangle, or export_name on a lang item item if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) - && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) + && codegen_fn_attrs.symbol_name.is_some() { let mut err = tcx .dcx() .struct_span_err( interesting_spans.no_mangle.unwrap_or_default(), - "`#[no_mangle]` cannot be used on internal language items", + "`#[no_mangle]` and `#[export_name]` cannot be used on internal language items", ) .with_note("Rustc requires this item to have a specific mangled name.") .with_span_label(tcx.def_span(did), "should be the internal language item"); @@ -574,6 +579,10 @@ fn handle_lang_items( } err.emit(); } + + if let Some(link_name_override) = link_name_override { + codegen_fn_attrs.symbol_name = Some(link_name_override); + } } /// Generate the [`CodegenFnAttrs`] for an item (identified by the [`LocalDefId`]). diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index b6ae4a98a34e3..a3460a1326eb9 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -36,10 +36,6 @@ impl<'tcx> TyCtxt<'tcx> { if let InstanceKind::Shim(ShimKind::Reify(_, _)) = instance_kind && attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER) { - if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { - attrs.to_mut().flags.remove(CodegenFnAttrFlags::NO_MANGLE); - } - if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { attrs.to_mut().flags.remove(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); } @@ -203,9 +199,6 @@ bitflags::bitflags! { /// `#[naked]`: an indicator to LLVM that no function prologue/epilogue /// should be generated. const NAKED = 1 << 2; - /// `#[no_mangle]`: an indicator that the function's name should be the same - /// as its symbol. - const NO_MANGLE = 1 << 3; /// `#[rustc_std_internal_symbol]`: an indicator that this symbol is a /// "weird symbol" for the standard library in that it has slightly /// different linkage, visibility, and reachability rules. @@ -290,8 +283,7 @@ impl CodegenFnAttrs { return false; } - self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) - || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + 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) diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 482848578a81b..a9f2375d0fa21 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -221,11 +221,6 @@ pub fn symbol_name_from_attrs<'tcx>( // Use provided name return Some(name.to_string()); } - - if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { - // Don't mangle - return Some(tcx.item_name(def_id).to_string()); - } } None diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 683e9095f9b0c..3d8cda11c3d85 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -136,7 +136,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let attrs = tcx.codegen_fn_attrs(def_id); // Skip over items without an explicitly defined symbol name. if !(attrs.symbol_name.is_some() - || attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) || attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)) { return interp_ok(()); From 4125bce958d4849392524bbe65443d289b8609ed Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 16 Jul 2026 15:30:10 +0200 Subject: [PATCH 2/6] Fix clashing_extern_declarations lint --- compiler/rustc_lint/src/foreign_modules.rs | 52 ++++++++-------------- 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 3010eadb61057..373371c625aa9 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -77,23 +77,11 @@ struct ClashingExternDeclarations { seen_decls: UnordMap, } -/// Differentiate between whether the name for an extern decl came from the link_name attribute or -/// just from declaration itself. This is important because we don't want to report clashes on -/// symbol name if they don't actually clash because one or the other links against a symbol with a -/// different name. -enum SymbolName { - /// The name of the symbol + the span of the annotation which introduced the link name. - Link(Symbol, Span), - /// No link name, so just the name of the symbol. - Normal(Symbol), -} - -impl SymbolName { - fn get_name(&self) -> Symbol { - match self { - SymbolName::Link(s, _) | SymbolName::Normal(s) => *s, - } - } +struct SymbolName { + /// The name of the symbol that will be linked against. + link_name: Symbol, + /// The span of the annotation which introduced the link name. + span: Option, } impl ClashingExternDeclarations { @@ -140,7 +128,7 @@ impl ClashingExternDeclarations { // Finally, emit the diagnostic. let this = tcx.item_name(this_fi.owner_id.to_def_id()); - let orig = orig.get_name(); + let orig = orig.link_name; let previous_decl_label = get_relevant_span(tcx, existing_did); let mismatch_label = get_relevant_span(tcx, this_fi.owner_id); let sub = @@ -176,28 +164,24 @@ impl ClashingExternDeclarations { /// the name specified in a #[link_name = ...] attribute if one was specified, else, just the /// symbol's name. fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName { - if let Some((overridden_link_name, overridden_link_name_span)) = - tcx.codegen_fn_attrs(fi).symbol_name.map(|overridden_link_name| { - // FIXME: Instead of searching through the attributes again to get span - // information, we could have codegen_fn_attrs also give span information back for - // where the attribute was defined. However, until this is found to be a - // bottleneck, this does just fine. - (overridden_link_name, find_attr!(tcx, fi, LinkName {span, ..} => *span).unwrap()) - }) - { - SymbolName::Link(overridden_link_name, overridden_link_name_span) - } else { - SymbolName::Normal(tcx.item_name(fi.to_def_id())) + // FIXME if symbol_name is not set, this is likely a #[rustc_std_internal_symbol] or EII which + // actually have their name mangled and thus should use the mangled name here. + let link_name = + tcx.codegen_fn_attrs(fi).symbol_name.unwrap_or_else(|| tcx.item_name(fi.to_def_id())); + SymbolName { + link_name, + // FIXME: Instead of searching through the attributes again to get span + // information, we could have codegen_fn_attrs also give span information back for + // where the attribute was defined. However, until this is found to be a + // bottleneck, this does just fine. + span: find_attr!(tcx, fi, LinkName {span, ..} => *span), } } /// We want to ensure that we use spans for both decls that include where the /// name was defined, whether that was from the link_name attribute or not. fn get_relevant_span(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> Span { - match name_of_extern_decl(tcx, fi) { - SymbolName::Normal(_) => tcx.def_span(fi), - SymbolName::Link(_, annot_span) => annot_span, - } + if let Some(span) = name_of_extern_decl(tcx, fi).span { span } else { tcx.def_span(fi) } } /// Checks whether two types are structurally the same enough that the declarations shouldn't From 104c06c93cc133cddb04801d3f2a3296cb0c794e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:21:10 +0200 Subject: [PATCH 3/6] Bless several tests --- tests/ui/codegen/no-mangle-on-internal-lang-items.rs | 10 +++------- .../ui/codegen/no-mangle-on-internal-lang-items.stderr | 6 +++--- tests/ui/codegen/no-mangle-on-panic-handler.rs | 4 ++-- tests/ui/codegen/no-mangle-on-panic-handler.stderr | 2 +- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/tests/ui/codegen/no-mangle-on-internal-lang-items.rs b/tests/ui/codegen/no-mangle-on-internal-lang-items.rs index 37766936410ed..58a03f04554f6 100644 --- a/tests/ui/codegen/no-mangle-on-internal-lang-items.rs +++ b/tests/ui/codegen/no-mangle-on-internal-lang-items.rs @@ -4,11 +4,7 @@ #![feature(rustc_attrs)] #[rustc_std_internal_symbol] -#[unsafe(no_mangle)] //~ERROR `#[no_mangle]` cannot be used on internal language items -fn internal_lang_function () { +#[unsafe(no_mangle)] //~ERROR `#[no_mangle]` and `#[export_name]` cannot be used on internal language items +fn internal_lang_function() {} -} - -fn main() { - -} +fn main() {} diff --git a/tests/ui/codegen/no-mangle-on-internal-lang-items.stderr b/tests/ui/codegen/no-mangle-on-internal-lang-items.stderr index 12461a6abb964..a02111673309c 100644 --- a/tests/ui/codegen/no-mangle-on-internal-lang-items.stderr +++ b/tests/ui/codegen/no-mangle-on-internal-lang-items.stderr @@ -1,10 +1,10 @@ -error: `#[no_mangle]` cannot be used on internal language items +error: `#[no_mangle]` and `#[export_name]` cannot be used on internal language items --> $DIR/no-mangle-on-internal-lang-items.rs:7:1 | LL | #[unsafe(no_mangle)] | ^^^^^^^^^^^^^^^^^^^^ -LL | fn internal_lang_function () { - | ---------------------------- should be the internal language item +LL | fn internal_lang_function() {} + | --------------------------- should be the internal language item | = note: Rustc requires this item to have a specific mangled name. diff --git a/tests/ui/codegen/no-mangle-on-panic-handler.rs b/tests/ui/codegen/no-mangle-on-panic-handler.rs index 1dc0cce0a2ece..6760c7a8bf045 100644 --- a/tests/ui/codegen/no-mangle-on-panic-handler.rs +++ b/tests/ui/codegen/no-mangle-on-panic-handler.rs @@ -1,13 +1,13 @@ // Issue an error when the user uses #[no_mangle] on the panic handler //@ edition:2024 -#![crate_type="lib"] +#![crate_type = "lib"] #![no_std] #![no_main] use core::panic::PanicInfo; -#[unsafe(no_mangle)] //~ ERROR `#[no_mangle]` cannot be used on internal language items +#[unsafe(no_mangle)] //~ ERROR `#[no_mangle]` and `#[export_name]` cannot be used on internal language items #[panic_handler] pub unsafe fn panic_fmt(pi: &PanicInfo) -> ! { loop {} diff --git a/tests/ui/codegen/no-mangle-on-panic-handler.stderr b/tests/ui/codegen/no-mangle-on-panic-handler.stderr index dc88b66d1b5d7..3808eb11f4cb6 100644 --- a/tests/ui/codegen/no-mangle-on-panic-handler.stderr +++ b/tests/ui/codegen/no-mangle-on-panic-handler.stderr @@ -1,4 +1,4 @@ -error: `#[no_mangle]` cannot be used on internal language items +error: `#[no_mangle]` and `#[export_name]` cannot be used on internal language items --> $DIR/no-mangle-on-panic-handler.rs:10:1 | LL | #[unsafe(no_mangle)] From 22ac858b150f7e572baed6d759d5948cb3c6b0f7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:29:41 +0200 Subject: [PATCH 4/6] Disable symbol name mangling for #[link_ordinal] #[link_ordinal] is not compatible with #[link_name] and items in extern blocks now have an implicit #[link_name] with the item name. Alternatively it would be possible to suppress the error message for this implicit #[link_name]. --- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 153dae08a4c03..5d7a4f73c86c3 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -405,6 +405,8 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code // 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 if codegen_fn_attrs.link_ordinal.is_some() { + // * `#[link_ordinal]` and `#[link_name]` are incompatible with each other } else { // NOTE: there's one more exception that we cannot apply here. On wasm, // some items cannot be `no_mangle`. From 66748bc1b2c11fbd33d62d10f7e2e0536a746b8e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 15 Jul 2026 14:50:16 +0200 Subject: [PATCH 5/6] Change foreign_item_symbol_aliases to an Option --- compiler/rustc_codegen_llvm/src/mono_item.rs | 154 +++++++++--------- .../src/back/symbol_export.rs | 4 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 3 +- .../src/middle/codegen_fn_attrs.rs | 4 +- compiler/rustc_passes/src/reachable.rs | 4 +- 5 files changed, 87 insertions(+), 82 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 19d43797a875d..f4c3913be709b 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -51,7 +51,9 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { self.assume_dso_local(g, false); let attrs = self.tcx.codegen_instance_attrs(instance.def); - self.add_static_aliases(g, &attrs.foreign_item_symbol_aliases); + if let Some(alias) = &attrs.foreign_item_symbol_alias { + self.add_static_alias(g, alias); + } self.instances.borrow_mut().insert(instance, g); } @@ -69,7 +71,9 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { let lldecl = self.predefine_without_aliases(instance, &attrs, linkage, visibility, symbol_name); - self.add_function_aliases(instance, lldecl, &attrs, &attrs.foreign_item_symbol_aliases); + if let Some(alias) = &attrs.foreign_item_symbol_alias { + self.add_function_alias(instance, lldecl, &attrs, alias); + } self.instances.borrow_mut().insert(instance, lldecl); } @@ -130,89 +134,89 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// So for statics we do want to use LLVM aliases, which is fine, /// since for those we don't care about target architecture anyway. /// - /// So, this function is for static aliases. See [`add_function_aliases`](Self::add_function_aliases) for the alternative. - fn add_static_aliases(&self, aliasee: &llvm::Value, aliases: &[(DefId, Linkage, Visibility)]) { + /// So, this function is for static aliases. See [`add_function_alias`](Self::add_function_alias) for the alternative. + fn add_static_alias( + &self, + aliasee: &llvm::Value, + (alias, linkage, visibility): &(DefId, Linkage, Visibility), + ) { let ty = self.get_type_of_global(aliasee); - for (alias, linkage, visibility) in aliases { - 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( - self.llmod, - ty, - AddressSpace::ZERO, - 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)); - } + 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( + self.llmod, + ty, + AddressSpace::ZERO, + 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)); } - /// See [`add_static_aliases`](Self::add_static_aliases) for docs. - fn add_function_aliases( + /// See [`add_static_alias`](Self::add_static_alias) for docs. + fn add_function_alias( &self, aliasee_instance: Instance<'tcx>, aliasee: &'ll llvm::Value, attrs: &Cow<'_, CodegenFnAttrs>, - aliases: &[(DefId, Linkage, Visibility)], + (alias, linkage, visibility): &(DefId, Linkage, Visibility), ) { - for (alias, linkage, visibility) in aliases { - let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias)); - tracing::debug!( - "FUNCTION ALIAS: generating fn {} that calls {aliasee_instance:?} ({alias:?} {linkage:?} {visibility:?})", - symbol_name.name - ); - - // predefine another copy of the original instance - // with a new symbol name - let alias_lldecl = self.predefine_without_aliases( - aliasee_instance, - attrs, - *linkage, - *visibility, - symbol_name.name, - ); - - let fn_abi: &FnAbi<'tcx, Ty<'tcx>> = - self.fn_abi_of_instance(aliasee_instance, ty::List::empty()); - - // both the alias and the aliasee have the same ty - let fn_ty = fn_abi.llvm_type(self); - let start_llbb = Builder::append_block(self, alias_lldecl, "start"); - let mut start_bx = Builder::build(self, start_llbb); - - let num_params = llvm::count_params(alias_lldecl); - let mut args = Vec::with_capacity(num_params as usize); - for index in 0..num_params { - args.push(llvm::get_param(alias_lldecl, index)); - } + let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias)); + tracing::debug!( + "FUNCTION ALIAS: generating fn {} that calls {aliasee_instance:?} ({alias:?} {linkage:?} {visibility:?})", + symbol_name.name + ); + + // predefine another copy of the original instance + // with a new symbol name + let alias_lldecl = self.predefine_without_aliases( + aliasee_instance, + attrs, + *linkage, + *visibility, + symbol_name.name, + ); + + let fn_abi: &FnAbi<'tcx, Ty<'tcx>> = + self.fn_abi_of_instance(aliasee_instance, ty::List::empty()); + + // both the alias and the aliasee have the same ty + let fn_ty = fn_abi.llvm_type(self); + let start_llbb = Builder::append_block(self, alias_lldecl, "start"); + let mut start_bx = Builder::build(self, start_llbb); + + let num_params = llvm::count_params(alias_lldecl); + let mut args = Vec::with_capacity(num_params as usize); + for index in 0..num_params { + args.push(llvm::get_param(alias_lldecl, index)); + } - let call = start_bx.call( - fn_ty, - Some(attrs), - Some(fn_abi), - aliasee, - &args, - None, - Some(aliasee_instance), - ); - - match &fn_abi.ret.mode { - PassMode::Ignore | PassMode::Indirect { .. } => start_bx.ret_void(), - PassMode::Direct(_) | PassMode::Pair { .. } | PassMode::Cast { .. } => { - start_bx.ret(call) - } + let call = start_bx.call( + fn_ty, + Some(attrs), + Some(fn_abi), + aliasee, + &args, + None, + Some(aliasee_instance), + ); + + match &fn_abi.ret.mode { + PassMode::Ignore | PassMode::Indirect { .. } => start_bx.ret_void(), + PassMode::Direct(_) | PassMode::Pair { .. } | PassMode::Cast { .. } => { + start_bx.ret(call) } } } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index bad8600905e3e..b1a57eed7a4bd 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -222,8 +222,8 @@ pub fn exported_non_generic_symbols_helper<'tcx>( } symbols.extend(sorted.iter().flat_map(|&(&def_id, &info)| { - tcx.codegen_fn_attrs(def_id).foreign_item_symbol_aliases.iter().map( - move |&(foreign_item, _linkage, _visibility)| { + tcx.codegen_fn_attrs(def_id).foreign_item_symbol_alias.map( + move |(foreign_item, _linkage, _visibility)| { (ExportedSymbol::NonGeneric(foreign_item), info) }, ) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 5d7a4f73c86c3..2c6e0dd0a2517 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -258,7 +258,8 @@ fn process_builtin_attrs( continue; } - codegen_fn_attrs.foreign_item_symbol_aliases.push(( + assert!(codegen_fn_attrs.foreign_item_symbol_alias.is_none()); + codegen_fn_attrs.foreign_item_symbol_alias = Some(( foreign_item, if i.is_default { Linkage::WeakAny } else { Linkage::External }, Visibility::Default, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index a3460a1326eb9..27a125e0f337a 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -80,7 +80,7 @@ pub struct CodegenFnAttrs { /// generate this function under its real name, /// but *also* under the same name as this foreign function so that the foreign function has an implementation. // FIXME: make "SymbolName<'tcx>" - pub foreign_item_symbol_aliases: Vec<(DefId, Linkage, Visibility)>, + pub foreign_item_symbol_alias: Option<(DefId, Linkage, Visibility)>, /// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an /// imported function has in the dynamic library. Note that this must not /// be set when `link_name` is set. This is for foreign items with the @@ -256,7 +256,7 @@ impl CodegenFnAttrs { symbol_name: None, link_ordinal: None, target_features: vec![], - foreign_item_symbol_aliases: vec![], + foreign_item_symbol_alias: None, safe_target_features: false, linkage: None, import_linkage: None, diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index b8497aefb7767..142ce73d1b957 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -189,7 +189,7 @@ impl<'tcx> ReachableContext<'tcx> { // (hence alias) that may be in another crate. These functions are marked as always-reachable since // it's very hard to track whether the original foreign item was reachable. It may live in another crate // and may be reachable from sibling crates. - let has_foreign_aliases_eii = !codegen_attrs.foreign_item_symbol_aliases.is_empty(); + let has_foreign_aliases_eii = codegen_attrs.foreign_item_symbol_alias.is_some(); if is_extern || has_foreign_aliases_eii { self.reachable_symbols.insert(search_item); } @@ -453,7 +453,7 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // (hence alias) that may be in another crate. These functions are marked as always-reachable since // it's very hard to track whether the original foreign item was reachable. It may live in another crate // and may be reachable from sibling crates. - || !codegen_attrs.foreign_item_symbol_aliases.is_empty() + || codegen_attrs.foreign_item_symbol_alias.is_some() } /// See module-level doc comment above. From 3b34082031aa6a92957410ebce889fac6c695795 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 15 Jul 2026 15:20:37 +0200 Subject: [PATCH 6/6] Rename symbols rather than add symbol aliases for EIIs when possible The symbol name of the EII impl will now get changed to match the EII decl whenever there is no default impl. This already works across all backends and targets unlike the weak symbol aliases that we currently use. When there is a default impl, we will still use weak symbol aliases. --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 21 ++++++++++++------- .../src/middle/codegen_fn_attrs.rs | 2 -- compiler/rustc_passes/src/reachable.rs | 15 +------------ 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 2c6e0dd0a2517..da1eb59f0ba70 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -13,10 +13,10 @@ use rustc_middle::middle::codegen_fn_attrs::{ }; use rustc_middle::mono::Visibility; use rustc_middle::query::Providers; -use rustc_middle::ty::{self as ty, TyCtxt}; +use rustc_middle::ty::{self as ty, Instance, TyCtxt}; use rustc_session::diagnostics::feature_err; use rustc_session::lint; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use rustc_target::spec::Os; use crate::diagnostics; @@ -258,12 +258,17 @@ fn process_builtin_attrs( continue; } - assert!(codegen_fn_attrs.foreign_item_symbol_alias.is_none()); - codegen_fn_attrs.foreign_item_symbol_alias = Some(( - foreign_item, - if i.is_default { Linkage::WeakAny } else { Linkage::External }, - Visibility::Default, - )); + if !i.is_default { + // FIXME is tcx.symbol_name() here safe or should we move this to + // rustc_symbol_mangling? + codegen_fn_attrs.symbol_name = Some(Symbol::intern( + tcx.symbol_name(Instance::mono(tcx, foreign_item)).name, + )); + } else { + assert!(codegen_fn_attrs.foreign_item_symbol_alias.is_none()); + codegen_fn_attrs.foreign_item_symbol_alias = + Some((foreign_item, Linkage::WeakAny, Visibility::Default)); + } codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; // If the declaration is `#[track_caller]`, derive it onto the implementation diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 27a125e0f337a..7688887b3b90d 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -284,8 +284,6 @@ impl CodegenFnAttrs { } 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 { diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 142ce73d1b957..8d12cd6f0e06e 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -183,14 +183,7 @@ impl<'tcx> ReachableContext<'tcx> { } else { CodegenFnAttrs::EMPTY }; - let is_extern = codegen_attrs.contains_extern_indicator(); - // Right now, the only way to get "foreign item symbol aliases" is by being an EII-implementation. - // EII implementations will generate under their own name but also under the name of some foreign item - // (hence alias) that may be in another crate. These functions are marked as always-reachable since - // it's very hard to track whether the original foreign item was reachable. It may live in another crate - // and may be reachable from sibling crates. - let has_foreign_aliases_eii = codegen_attrs.foreign_item_symbol_alias.is_some(); - if is_extern || has_foreign_aliases_eii { + if codegen_attrs.contains_extern_indicator() { self.reachable_symbols.insert(search_item); } } else { @@ -448,12 +441,6 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // across all crates. || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) - // Right now, the only way to get "foreign item symbol aliases" is by being an EII-implementation. - // EII implementations will generate under their own name but also under the name of some foreign item - // (hence alias) that may be in another crate. These functions are marked as always-reachable since - // it's very hard to track whether the original foreign item was reachable. It may live in another crate - // and may be reachable from sibling crates. - || codegen_attrs.foreign_item_symbol_alias.is_some() } /// See module-level doc comment above.