authorbors <bors@rust-lang.org> 2025-08-12 16:20:24 UTC
committerbors <bors@rust-lang.org> 2025-08-12 16:20:24 UTC
log8e62bfd311791bfd9dca886abdfbab07ec54d8b4
tree9d7e5b411e8a1f0baa78561850adc5314a246986
parentd9dba3a55476ae2da5d4e5bce8a81b341c675750
parent7aa8707639ede0605d7182bc13047b6e4549e5b1

Auto merge of #144678 - jdonszelmann:no-mangle-extern, r=bjorn3

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
569569 // core/std/allocators/etc. For example symbols used to hook up allocation
570570 // are not considered for export
571571 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);
573573 let std_internal =
574574 codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
575575
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
443443 if tcx.should_inherit_track_caller(did) {
444444 codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
445445 }
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 }
446467}
447468
448469fn check_result(
compiler/rustc_middle/src/middle/codegen_fn_attrs.rs+6-1
......@@ -3,6 +3,7 @@ use std::borrow::Cow;
33use rustc_abi::Align;
44use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs;
55use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr};
6use rustc_hir::def_id::DefId;
67use rustc_macros::{HashStable, TyDecodable, TyEncodable};
78use rustc_span::Symbol;
89use rustc_target::spec::SanitizerSet;
......@@ -193,7 +194,11 @@ impl CodegenFnAttrs {
193194 /// * `#[linkage]` is present
194195 ///
195196 /// 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
197202 self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
198203 || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
199204 || self.export_name.is_some()
compiler/rustc_middle/src/mir/mono.rs+1-1
......@@ -151,7 +151,7 @@ impl<'tcx> MonoItem<'tcx> {
151151 // instantiation:
152152 // We emit an unused_attributes lint for this case, which should be kept in sync if possible.
153153 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())
155155 || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED)
156156 {
157157 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 {
1818 let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id);
1919 // If this has an extern indicator, then this function is globally shared and thus will not
2020 // 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()) {
2222 return false;
2323 }
2424
compiler/rustc_passes/src/check_attr.rs+18-18
......@@ -563,7 +563,24 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
563563 match target {
564564 Target::Fn
565565 | 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 }
567584 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
568585 self.tcx.emit_node_span_lint(
569586 UNUSED_ATTRIBUTES,
......@@ -590,23 +607,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
590607 self.dcx().emit_err(errors::InlineNotFnOrClosure { attr_span, defn_span });
591608 }
592609 }
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 }
610610 }
611611
612612 /// 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(
703703
704704 // #[used], #[no_mangle], #[export_name], etc also keeps the item alive
705705 // 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())
707707 || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
708708 || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
709709 }
compiler/rustc_passes/src/reachable.rs+3-2
......@@ -183,7 +183,7 @@ impl<'tcx> ReachableContext<'tcx> {
183183 } else {
184184 CodegenFnAttrs::EMPTY
185185 };
186 let is_extern = codegen_attrs.contains_extern_indicator();
186 let is_extern = codegen_attrs.contains_extern_indicator(self.tcx, search_item.into());
187187 if is_extern {
188188 self.reachable_symbols.insert(search_item);
189189 }
......@@ -423,8 +423,9 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
423423 if !tcx.def_kind(def_id).has_codegen_attrs() {
424424 return false;
425425 }
426
426427 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())
428429 // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by
429430 // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their
430431 // `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>(
218218 }
219219 }
220220
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
242245 {
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();
247248 }
248249
249250 if let Some(name) = attrs.export_name {
......@@ -251,7 +252,9 @@ fn compute_symbol_name<'tcx>(
251252 return name.to_string();
252253 }
253254
254 if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
255 if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
256 && !wasm_import_module_exception_force_mangling
257 {
255258 // Don't mangle
256259 return tcx.item_name(def_id).to_string();
257260 }
src/tools/clippy/clippy_lints/src/missing_inline.rs+1-1
......@@ -190,5 +190,5 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
190190/// and a rustc warning would be triggered, see #15301
191191fn fn_is_externally_exported(cx: &LateContext<'_>, def_id: DefId) -> bool {
192192 let attrs = cx.tcx.codegen_fn_attrs(def_id);
193 attrs.contains_extern_indicator()
193 attrs.contains_extern_indicator(cx.tcx, def_id)
194194}
src/tools/miri/src/bin/miri.rs+1-1
......@@ -279,7 +279,7 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
279279 return None;
280280 }
281281 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())
283283 || codegen_fn_attrs
284284 .flags
285285 .contains(CodegenFnAttrFlags::USED_COMPILER)
src/tools/miri/src/helpers.rs+1-1
......@@ -132,7 +132,7 @@ pub fn iter_exported_symbols<'tcx>(
132132 for def_id in crate_items.definitions() {
133133 let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
134134 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())
136136 || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
137137 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
138138 || 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) {
2424
2525 let bytes = rfs::read("foo.wasm");
2626 println!("{}", bytes.len());
27 assert!(bytes.len() < 40_000);
27 assert!(bytes.len() < 40_000, "bytes len was: {}", bytes.len());
2828}