| author | bors <bors@rust-lang.org> 2025-08-12 16:20:24 UTC |
| committer | bors <bors@rust-lang.org> 2025-08-12 16:20:24 UTC |
| log | 8e62bfd311791bfd9dca886abdfbab07ec54d8b4 |
| tree | 9d7e5b411e8a1f0baa78561850adc5314a246986 |
| parent | d9dba3a55476ae2da5d4e5bce8a81b341c675750 |
| parent | 7aa8707639ede0605d7182bc13047b6e4549e5b1 |
Make no_mangle on foreign items explicit instead of implicit
for a followup PR I'm working on I need some foreign items to mangle. I could add a new attribute: `no_no_mangle` or something silly like that but by explicitly putting `no_mangle` in the codegen fn attrs of foreign items we can default it to `no_mangle` and then easily remove it when we don't want it.
I guess you'd know about this r? `@bjorn3.` Shouldn't be too hard to review :)
Builds on rust-lang/rust#144655 which should merge first.13 files changed, 85 insertions(+), 55 deletions(-)
compiler/rustc_codegen_ssa/src/back/symbol_export.rs+1-1| ... | ... | @@ -569,7 +569,7 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel |
| 569 | 569 | // core/std/allocators/etc. For example symbols used to hook up allocation |
| 570 | 570 | // are not considered for export |
| 571 | 571 | let codegen_fn_attrs = tcx.codegen_fn_attrs(sym_def_id); |
| 572 | let is_extern = codegen_fn_attrs.contains_extern_indicator(); | |
| 572 | let is_extern = codegen_fn_attrs.contains_extern_indicator(tcx, sym_def_id); | |
| 573 | 573 | let std_internal = |
| 574 | 574 | codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); |
| 575 | 575 |
compiler/rustc_codegen_ssa/src/codegen_attrs.rs+21| ... | ... | @@ -443,6 +443,27 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code |
| 443 | 443 | if tcx.should_inherit_track_caller(did) { |
| 444 | 444 | codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; |
| 445 | 445 | } |
| 446 | ||
| 447 | // Foreign items by default use no mangling for their symbol name. | |
| 448 | if tcx.is_foreign_item(did) { | |
| 449 | // There's a few exceptions to this rule though: | |
| 450 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { | |
| 451 | // * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way | |
| 452 | // both for exports and imports through foreign items. This is handled further, | |
| 453 | // during symbol mangling logic. | |
| 454 | } else if codegen_fn_attrs.link_name.is_some() { | |
| 455 | // * This can be overridden with the `#[link_name]` attribute | |
| 456 | } else { | |
| 457 | // NOTE: there's one more exception that we cannot apply here. On wasm, | |
| 458 | // some items cannot be `no_mangle`. | |
| 459 | // However, we don't have enough information here to determine that. | |
| 460 | // As such, no_mangle foreign items on wasm that have the same defid as some | |
| 461 | // import will *still* be mangled despite this. | |
| 462 | // | |
| 463 | // if none of the exceptions apply; apply no_mangle | |
| 464 | codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; | |
| 465 | } | |
| 466 | } | |
| 446 | 467 | } |
| 447 | 468 | |
| 448 | 469 | fn check_result( |
compiler/rustc_middle/src/middle/codegen_fn_attrs.rs+6-1| ... | ... | @@ -3,6 +3,7 @@ use std::borrow::Cow; |
| 3 | 3 | use rustc_abi::Align; |
| 4 | 4 | use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs; |
| 5 | 5 | use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr}; |
| 6 | use rustc_hir::def_id::DefId; | |
| 6 | 7 | use rustc_macros::{HashStable, TyDecodable, TyEncodable}; |
| 7 | 8 | use rustc_span::Symbol; |
| 8 | 9 | use rustc_target::spec::SanitizerSet; |
| ... | ... | @@ -193,7 +194,11 @@ impl CodegenFnAttrs { |
| 193 | 194 | /// * `#[linkage]` is present |
| 194 | 195 | /// |
| 195 | 196 | /// Keep this in sync with the logic for the unused_attributes for `#[inline]` lint. |
| 196 | pub fn contains_extern_indicator(&self) -> bool { | |
| 197 | pub fn contains_extern_indicator(&self, tcx: TyCtxt<'_>, did: DefId) -> bool { | |
| 198 | if tcx.is_foreign_item(did) { | |
| 199 | return false; | |
| 200 | } | |
| 201 | ||
| 197 | 202 | self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) |
| 198 | 203 | || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) |
| 199 | 204 | || self.export_name.is_some() |
compiler/rustc_middle/src/mir/mono.rs+1-1| ... | ... | @@ -151,7 +151,7 @@ impl<'tcx> MonoItem<'tcx> { |
| 151 | 151 | // instantiation: |
| 152 | 152 | // We emit an unused_attributes lint for this case, which should be kept in sync if possible. |
| 153 | 153 | let codegen_fn_attrs = tcx.codegen_instance_attrs(instance.def); |
| 154 | if codegen_fn_attrs.contains_extern_indicator() | |
| 154 | if codegen_fn_attrs.contains_extern_indicator(tcx, instance.def.def_id()) | |
| 155 | 155 | || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) |
| 156 | 156 | { |
| 157 | 157 | return InstantiationMode::GloballyShared { may_conflict: false }; |
compiler/rustc_mir_transform/src/cross_crate_inline.rs+1-1| ... | ... | @@ -18,7 +18,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { |
| 18 | 18 | let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); |
| 19 | 19 | // If this has an extern indicator, then this function is globally shared and thus will not |
| 20 | 20 | // generate cgu-internal copies which would make it cross-crate inlinable. |
| 21 | if codegen_fn_attrs.contains_extern_indicator() { | |
| 21 | if codegen_fn_attrs.contains_extern_indicator(tcx, def_id.into()) { | |
| 22 | 22 | return false; |
| 23 | 23 | } |
| 24 | 24 |
compiler/rustc_passes/src/check_attr.rs+18-18| ... | ... | @@ -563,7 +563,24 @@ impl<'tcx> CheckAttrVisitor<'tcx> { |
| 563 | 563 | match target { |
| 564 | 564 | Target::Fn |
| 565 | 565 | | Target::Closure |
| 566 | | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} | |
| 566 | | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => { | |
| 567 | // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported. | |
| 568 | if let Some(did) = hir_id.as_owner() | |
| 569 | && self.tcx.def_kind(did).has_codegen_attrs() | |
| 570 | && kind != &InlineAttr::Never | |
| 571 | { | |
| 572 | let attrs = self.tcx.codegen_fn_attrs(did); | |
| 573 | // Not checking naked as `#[inline]` is forbidden for naked functions anyways. | |
| 574 | if attrs.contains_extern_indicator(self.tcx, did.into()) { | |
| 575 | self.tcx.emit_node_span_lint( | |
| 576 | UNUSED_ATTRIBUTES, | |
| 577 | hir_id, | |
| 578 | attr_span, | |
| 579 | errors::InlineIgnoredForExported {}, | |
| 580 | ); | |
| 581 | } | |
| 582 | } | |
| 583 | } | |
| 567 | 584 | Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { |
| 568 | 585 | self.tcx.emit_node_span_lint( |
| 569 | 586 | UNUSED_ATTRIBUTES, |
| ... | ... | @@ -590,23 +607,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { |
| 590 | 607 | self.dcx().emit_err(errors::InlineNotFnOrClosure { attr_span, defn_span }); |
| 591 | 608 | } |
| 592 | 609 | } |
| 593 | ||
| 594 | // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported. | |
| 595 | if let Some(did) = hir_id.as_owner() | |
| 596 | && self.tcx.def_kind(did).has_codegen_attrs() | |
| 597 | && kind != &InlineAttr::Never | |
| 598 | { | |
| 599 | let attrs = self.tcx.codegen_fn_attrs(did); | |
| 600 | // Not checking naked as `#[inline]` is forbidden for naked functions anyways. | |
| 601 | if attrs.contains_extern_indicator() { | |
| 602 | self.tcx.emit_node_span_lint( | |
| 603 | UNUSED_ATTRIBUTES, | |
| 604 | hir_id, | |
| 605 | attr_span, | |
| 606 | errors::InlineIgnoredForExported {}, | |
| 607 | ); | |
| 608 | } | |
| 609 | } | |
| 610 | 610 | } |
| 611 | 611 | |
| 612 | 612 | /// Checks that `#[coverage(..)]` is applied to a function/closure/method, |
compiler/rustc_passes/src/dead.rs+1-1| ... | ... | @@ -703,7 +703,7 @@ fn has_allow_dead_code_or_lang_attr( |
| 703 | 703 | |
| 704 | 704 | // #[used], #[no_mangle], #[export_name], etc also keeps the item alive |
| 705 | 705 | // forcefully, e.g., for placing it in a specific section. |
| 706 | cg_attrs.contains_extern_indicator() | |
| 706 | cg_attrs.contains_extern_indicator(tcx, def_id.into()) | |
| 707 | 707 | || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) |
| 708 | 708 | || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) |
| 709 | 709 | } |
compiler/rustc_passes/src/reachable.rs+3-2| ... | ... | @@ -183,7 +183,7 @@ impl<'tcx> ReachableContext<'tcx> { |
| 183 | 183 | } else { |
| 184 | 184 | CodegenFnAttrs::EMPTY |
| 185 | 185 | }; |
| 186 | let is_extern = codegen_attrs.contains_extern_indicator(); | |
| 186 | let is_extern = codegen_attrs.contains_extern_indicator(self.tcx, search_item.into()); | |
| 187 | 187 | if is_extern { |
| 188 | 188 | self.reachable_symbols.insert(search_item); |
| 189 | 189 | } |
| ... | ... | @@ -423,8 +423,9 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { |
| 423 | 423 | if !tcx.def_kind(def_id).has_codegen_attrs() { |
| 424 | 424 | return false; |
| 425 | 425 | } |
| 426 | ||
| 426 | 427 | let codegen_attrs = tcx.codegen_fn_attrs(def_id); |
| 427 | codegen_attrs.contains_extern_indicator() | |
| 428 | codegen_attrs.contains_extern_indicator(tcx, def_id.into()) | |
| 428 | 429 | // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by |
| 429 | 430 | // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their |
| 430 | 431 | // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs. |
compiler/rustc_symbol_mangling/src/lib.rs+29-26| ... | ... | @@ -218,32 +218,33 @@ fn compute_symbol_name<'tcx>( |
| 218 | 218 | } |
| 219 | 219 | } |
| 220 | 220 | |
| 221 | // Foreign items by default use no mangling for their symbol name. There's a | |
| 222 | // few exceptions to this rule though: | |
| 223 | // | |
| 224 | // * This can be overridden with the `#[link_name]` attribute | |
| 225 | // | |
| 226 | // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the | |
| 227 | // same-named symbol when imported from different wasm modules will get | |
| 228 | // hooked up incorrectly. As a result foreign symbols, on the wasm target, | |
| 229 | // with a wasm import module, get mangled. Additionally our codegen will | |
| 230 | // deduplicate symbols based purely on the symbol name, but for wasm this | |
| 231 | // isn't quite right because the same-named symbol on wasm can come from | |
| 232 | // different modules. For these reasons if `#[link(wasm_import_module)]` | |
| 233 | // is present we mangle everything on wasm because the demangled form will | |
| 234 | // show up in the `wasm-import-name` custom attribute in LLVM IR. | |
| 235 | // | |
| 236 | // * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way | |
| 237 | // both for exports and imports through foreign items. This is handled above. | |
| 238 | // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 | |
| 239 | if tcx.is_foreign_item(def_id) | |
| 240 | && (!tcx.sess.target.is_like_wasm | |
| 241 | || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id)) | |
| 221 | let wasm_import_module_exception_force_mangling = { | |
| 222 | // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the | |
| 223 | // same-named symbol when imported from different wasm modules will get | |
| 224 | // hooked up incorrectly. As a result foreign symbols, on the wasm target, | |
| 225 | // with a wasm import module, get mangled. Additionally our codegen will | |
| 226 | // deduplicate symbols based purely on the symbol name, but for wasm this | |
| 227 | // isn't quite right because the same-named symbol on wasm can come from | |
| 228 | // different modules. For these reasons if `#[link(wasm_import_module)]` | |
| 229 | // is present we mangle everything on wasm because the demangled form will | |
| 230 | // show up in the `wasm-import-name` custom attribute in LLVM IR. | |
| 231 | // | |
| 232 | // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 | |
| 233 | // | |
| 234 | // So, on wasm if a foreign item loses its `#[no_mangle]`, it might *still* | |
| 235 | // be mangled if we're forced to. Note: I don't like this. | |
| 236 | // These kinds of exceptions should be added during the `codegen_attrs` query. | |
| 237 | // However, we don't have the wasm import module map there yet. | |
| 238 | tcx.is_foreign_item(def_id) | |
| 239 | && tcx.sess.target.is_like_wasm | |
| 240 | && tcx.wasm_import_module_map(LOCAL_CRATE).contains_key(&def_id.into()) | |
| 241 | }; | |
| 242 | ||
| 243 | if let Some(name) = attrs.link_name | |
| 244 | && !wasm_import_module_exception_force_mangling | |
| 242 | 245 | { |
| 243 | if let Some(name) = attrs.link_name { | |
| 244 | return name.to_string(); | |
| 245 | } | |
| 246 | return tcx.item_name(def_id).to_string(); | |
| 246 | // Use provided name | |
| 247 | return name.to_string(); | |
| 247 | 248 | } |
| 248 | 249 | |
| 249 | 250 | if let Some(name) = attrs.export_name { |
| ... | ... | @@ -251,7 +252,9 @@ fn compute_symbol_name<'tcx>( |
| 251 | 252 | return name.to_string(); |
| 252 | 253 | } |
| 253 | 254 | |
| 254 | if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { | |
| 255 | if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) | |
| 256 | && !wasm_import_module_exception_force_mangling | |
| 257 | { | |
| 255 | 258 | // Don't mangle |
| 256 | 259 | return tcx.item_name(def_id).to_string(); |
| 257 | 260 | } |
src/tools/clippy/clippy_lints/src/missing_inline.rs+1-1| ... | ... | @@ -190,5 +190,5 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { |
| 190 | 190 | /// and a rustc warning would be triggered, see #15301 |
| 191 | 191 | fn fn_is_externally_exported(cx: &LateContext<'_>, def_id: DefId) -> bool { |
| 192 | 192 | let attrs = cx.tcx.codegen_fn_attrs(def_id); |
| 193 | attrs.contains_extern_indicator() | |
| 193 | attrs.contains_extern_indicator(cx.tcx, def_id) | |
| 194 | 194 | } |
src/tools/miri/src/bin/miri.rs+1-1| ... | ... | @@ -279,7 +279,7 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { |
| 279 | 279 | return None; |
| 280 | 280 | } |
| 281 | 281 | let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id); |
| 282 | if codegen_fn_attrs.contains_extern_indicator() | |
| 282 | if codegen_fn_attrs.contains_extern_indicator(tcx, local_def_id.into()) | |
| 283 | 283 | || codegen_fn_attrs |
| 284 | 284 | .flags |
| 285 | 285 | .contains(CodegenFnAttrFlags::USED_COMPILER) |
src/tools/miri/src/helpers.rs+1-1| ... | ... | @@ -132,7 +132,7 @@ pub fn iter_exported_symbols<'tcx>( |
| 132 | 132 | for def_id in crate_items.definitions() { |
| 133 | 133 | let exported = tcx.def_kind(def_id).has_codegen_attrs() && { |
| 134 | 134 | let codegen_attrs = tcx.codegen_fn_attrs(def_id); |
| 135 | codegen_attrs.contains_extern_indicator() | |
| 135 | codegen_attrs.contains_extern_indicator(tcx, def_id.into()) | |
| 136 | 136 | || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) |
| 137 | 137 | || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) |
| 138 | 138 | || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) |
tests/run-make/wasm-panic-small/rmake.rs+1-1| ... | ... | @@ -24,5 +24,5 @@ fn test(cfg: &str) { |
| 24 | 24 | |
| 25 | 25 | let bytes = rfs::read("foo.wasm"); |
| 26 | 26 | println!("{}", bytes.len()); |
| 27 | assert!(bytes.len() < 40_000); | |
| 27 | assert!(bytes.len() < 40_000, "bytes len was: {}", bytes.len()); | |
| 28 | 28 | } |