Skip to content

Commit 1342913

Browse files
committed
Auto merge of #54451 - alexcrichton:no-mangle-extern-linkage, r=michaelwoerister
rustc: Allow `#[no_mangle]` anywhere in a crate This commit updates the compiler to allow the `#[no_mangle]` (and `#[export_name]` attributes) to be located anywhere within a crate. These attributes are unconditionally processed, causing the compiler to always generate an exported symbol with the appropriate name. After some discussion on #54135 it was found that not a great reason this hasn't been allowed already, and it seems to match the behavior that many expect! Previously the compiler would only export a `#[no_mangle]` symbol if it were *publicly reachable*, meaning that it itself is `pub` and it's otherwise publicly reachable from the root of the crate. This new definition is that `#[no_mangle]` *is always reachable*, no matter where it is in a crate or whether it has `pub` or not. This should make it much easier to declare an exported symbol with a known and unique name, even when it's an internal implementation detail of the crate itself. Note that these symbols will persist beyond LTO as well, always making their way to the linker. Along the way this commit removes the `private_no_mangle_functions` lint (also for statics) as there's no longer any need to lint these situations. Furthermore a good number of tests were updated now that symbol visibility has been changed. Closes #54135
2 parents dbecb7a + d7d7045 commit 1342913

22 files changed

+400
-307
lines changed

src/librustc/hir/mod.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -2467,9 +2467,22 @@ impl CodegenFnAttrs {
24672467
}
24682468
}
24692469

2470-
/// True if `#[no_mangle]` or `#[export_name(...)]` is present.
2470+
/// True if it looks like this symbol needs to be exported, for example:
2471+
///
2472+
/// * `#[no_mangle]` is present
2473+
/// * `#[export_name(...)]` is present
2474+
/// * `#[linkage]` is present
24712475
pub fn contains_extern_indicator(&self) -> bool {
2472-
self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) || self.export_name.is_some()
2476+
self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) ||
2477+
self.export_name.is_some() ||
2478+
match self.linkage {
2479+
// these are private, make sure we don't try to consider
2480+
// them external
2481+
None |
2482+
Some(Linkage::Internal) |
2483+
Some(Linkage::Private) => false,
2484+
Some(_) => true,
2485+
}
24732486
}
24742487
}
24752488

src/librustc/middle/dead.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use hir::intravisit::{self, Visitor, NestedVisitorMap};
1818
use hir::itemlikevisit::ItemLikeVisitor;
1919

2020
use hir::def::Def;
21+
use hir::CodegenFnAttrFlags;
2122
use hir::def_id::{DefId, LOCAL_CRATE};
2223
use lint;
2324
use middle::privacy;
@@ -302,14 +303,18 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_, '_, '_>,
302303
return true;
303304
}
304305

305-
// #[used] also keeps the item alive forcefully,
306-
// e.g. for placing it in a specific section.
307-
if attr::contains_name(attrs, "used") {
306+
// Don't lint about global allocators
307+
if attr::contains_name(attrs, "global_allocator") {
308308
return true;
309309
}
310310

311-
// Don't lint about global allocators
312-
if attr::contains_name(attrs, "global_allocator") {
311+
let def_id = tcx.hir.local_def_id(id);
312+
let cg_attrs = tcx.codegen_fn_attrs(def_id);
313+
314+
// #[used], #[no_mangle], #[export_name], etc also keeps the item alive
315+
// forcefully, e.g. for placing it in a specific section.
316+
if cg_attrs.contains_extern_indicator() ||
317+
cg_attrs.flags.contains(CodegenFnAttrFlags::USED) {
313318
return true;
314319
}
315320

src/librustc/middle/reachable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a,
352352
// which are currently akin to allocator symbols.
353353
let def_id = self.tcx.hir.local_def_id(item.id);
354354
let codegen_attrs = self.tcx.codegen_fn_attrs(def_id);
355-
if codegen_attrs.linkage.is_some() ||
355+
if codegen_attrs.contains_extern_indicator() ||
356356
codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
357357
self.worklist.push(item.id);
358358
}

src/librustc_lint/builtin.rs

+1-60
Original file line numberDiff line numberDiff line change
@@ -1163,18 +1163,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PluginAsLibrary {
11631163
}
11641164
}
11651165

1166-
declare_lint! {
1167-
PRIVATE_NO_MANGLE_FNS,
1168-
Warn,
1169-
"functions marked #[no_mangle] should be exported"
1170-
}
1171-
1172-
declare_lint! {
1173-
PRIVATE_NO_MANGLE_STATICS,
1174-
Warn,
1175-
"statics marked #[no_mangle] should be exported"
1176-
}
1177-
11781166
declare_lint! {
11791167
NO_MANGLE_CONST_ITEMS,
11801168
Deny,
@@ -1192,52 +1180,16 @@ pub struct InvalidNoMangleItems;
11921180

11931181
impl LintPass for InvalidNoMangleItems {
11941182
fn get_lints(&self) -> LintArray {
1195-
lint_array!(PRIVATE_NO_MANGLE_FNS,
1196-
PRIVATE_NO_MANGLE_STATICS,
1197-
NO_MANGLE_CONST_ITEMS,
1183+
lint_array!(NO_MANGLE_CONST_ITEMS,
11981184
NO_MANGLE_GENERIC_ITEMS)
11991185
}
12001186
}
12011187

12021188
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
12031189
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
1204-
let suggest_export = |vis: &hir::Visibility, err: &mut DiagnosticBuilder| {
1205-
let suggestion = match vis.node {
1206-
hir::VisibilityKind::Inherited => {
1207-
// inherited visibility is empty span at item start; need an extra space
1208-
Some("pub ".to_owned())
1209-
},
1210-
hir::VisibilityKind::Restricted { .. } |
1211-
hir::VisibilityKind::Crate(_) => {
1212-
Some("pub".to_owned())
1213-
},
1214-
hir::VisibilityKind::Public => {
1215-
err.help("try exporting the item with a `pub use` statement");
1216-
None
1217-
}
1218-
};
1219-
if let Some(replacement) = suggestion {
1220-
err.span_suggestion_with_applicability(
1221-
vis.span,
1222-
"try making it public",
1223-
replacement,
1224-
Applicability::MachineApplicable
1225-
);
1226-
}
1227-
};
1228-
12291190
match it.node {
12301191
hir::ItemKind::Fn(.., ref generics, _) => {
12311192
if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, "no_mangle") {
1232-
if attr::contains_name(&it.attrs, "linkage") {
1233-
return;
1234-
}
1235-
if !cx.access_levels.is_reachable(it.id) {
1236-
let msg = "function is marked #[no_mangle], but not exported";
1237-
let mut err = cx.struct_span_lint(PRIVATE_NO_MANGLE_FNS, it.span, msg);
1238-
suggest_export(&it.vis, &mut err);
1239-
err.emit();
1240-
}
12411193
for param in &generics.params {
12421194
match param.kind {
12431195
GenericParamKind::Lifetime { .. } => {}
@@ -1261,15 +1213,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
12611213
}
12621214
}
12631215
}
1264-
hir::ItemKind::Static(..) => {
1265-
if attr::contains_name(&it.attrs, "no_mangle") &&
1266-
!cx.access_levels.is_reachable(it.id) {
1267-
let msg = "static is marked #[no_mangle], but not exported";
1268-
let mut err = cx.struct_span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, msg);
1269-
suggest_export(&it.vis, &mut err);
1270-
err.emit();
1271-
}
1272-
}
12731216
hir::ItemKind::Const(..) => {
12741217
if attr::contains_name(&it.attrs, "no_mangle") {
12751218
// Const items do not refer to a particular location in memory, and therefore
@@ -1785,8 +1728,6 @@ impl LintPass for SoftLints {
17851728
UNUSED_DOC_COMMENTS,
17861729
UNCONDITIONAL_RECURSION,
17871730
PLUGIN_AS_LIBRARY,
1788-
PRIVATE_NO_MANGLE_FNS,
1789-
PRIVATE_NO_MANGLE_STATICS,
17901731
NO_MANGLE_CONST_ITEMS,
17911732
NO_MANGLE_GENERIC_ITEMS,
17921733
MUTABLE_TRANSMUTES,

src/librustc_lint/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -387,4 +387,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
387387
"converted into hard error, see https://github.com/rust-lang/rust/issues/48950");
388388
store.register_removed("resolve_trait_on_defaulted_unit",
389389
"converted into hard error, see https://github.com/rust-lang/rust/issues/48950");
390+
store.register_removed("private_no_mangle_fns",
391+
"no longer an warning, #[no_mangle] functions always exported");
392+
store.register_removed("private_no_mangle_statics",
393+
"no longer an warning, #[no_mangle] statics always exported");
390394
}

src/libstd/panicking.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -515,11 +515,11 @@ pub fn update_count_then_panic(msg: Box<dyn Any + Send>) -> ! {
515515
rust_panic(&mut RewrapBox(msg))
516516
}
517517

518-
/// A private no-mangle function on which to slap yer breakpoints.
518+
/// An unmangled function (through `rustc_std_internal_symbol`) on which to slap
519+
/// yer breakpoints.
519520
#[inline(never)]
520-
#[no_mangle]
521-
#[allow(private_no_mangle_fns)] // yes we get it, but we like breakpoints
522-
pub fn rust_panic(mut msg: &mut dyn BoxMeUp) -> ! {
521+
#[cfg_attr(not(test), rustc_std_internal_symbol)]
522+
fn rust_panic(mut msg: &mut dyn BoxMeUp) -> ! {
523523
let code = unsafe {
524524
let obj = &mut msg as *mut &mut dyn BoxMeUp;
525525
__rust_start_panic(obj as usize)

src/test/codegen/export-no-mangle.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type = "lib"]
12+
13+
mod private {
14+
// CHECK: @FOO =
15+
#[no_mangle]
16+
pub static FOO: u32 = 3;
17+
18+
// CHECK: @BAR =
19+
#[export_name = "BAR"]
20+
static BAR: u32 = 3;
21+
22+
// CHECK: void @foo()
23+
#[no_mangle]
24+
pub extern fn foo() {}
25+
26+
// CHECK: void @bar()
27+
#[export_name = "bar"]
28+
extern fn bar() {}
29+
}
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -O
12+
// `#[no_mangle]`d functions always have external linkage, i.e. no `internal` in their `define`s
13+
14+
#![crate_type = "lib"]
15+
#![no_std]
16+
17+
// CHECK: define void @a()
18+
#[no_mangle]
19+
fn a() {}
20+
21+
// CHECK: define void @b()
22+
#[no_mangle]
23+
pub fn b() {}
24+
25+
mod private {
26+
// CHECK: define void @c()
27+
#[no_mangle]
28+
fn c() {}
29+
30+
// CHECK: define void @d()
31+
#[no_mangle]
32+
pub fn d() {}
33+
}
34+
35+
const HIDDEN: () = {
36+
// CHECK: define void @e()
37+
#[no_mangle]
38+
fn e() {}
39+
40+
// CHECK: define void @f()
41+
#[no_mangle]
42+
pub fn f() {}
43+
};
44+
45+
// The surrounding item should not accidentally become external
46+
// CHECK: define internal {{.*}} void @_ZN22external_no_mangle_fns1x
47+
#[inline(never)]
48+
fn x() {
49+
// CHECK: define void @g()
50+
#[no_mangle]
51+
fn g() {
52+
x();
53+
}
54+
55+
// CHECK: define void @h()
56+
#[no_mangle]
57+
pub fn h() {}
58+
59+
// side effect to keep `x` around
60+
unsafe {
61+
core::ptr::read_volatile(&42);
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -O
12+
// `#[no_mangle]`d static variables always have external linkage, i.e. no `internal` in their
13+
// definitions
14+
15+
#![crate_type = "lib"]
16+
#![no_std]
17+
18+
// CHECK: @A = local_unnamed_addr constant
19+
#[no_mangle]
20+
static A: u8 = 0;
21+
22+
// CHECK: @B = local_unnamed_addr global
23+
#[no_mangle]
24+
static mut B: u8 = 0;
25+
26+
// CHECK: @C = local_unnamed_addr constant
27+
#[no_mangle]
28+
pub static C: u8 = 0;
29+
30+
// CHECK: @D = local_unnamed_addr global
31+
#[no_mangle]
32+
pub static mut D: u8 = 0;
33+
34+
mod private {
35+
// CHECK: @E = local_unnamed_addr constant
36+
#[no_mangle]
37+
static E: u8 = 0;
38+
39+
// CHECK: @F = local_unnamed_addr global
40+
#[no_mangle]
41+
static mut F: u8 = 0;
42+
43+
// CHECK: @G = local_unnamed_addr constant
44+
#[no_mangle]
45+
pub static G: u8 = 0;
46+
47+
// CHECK: @H = local_unnamed_addr global
48+
#[no_mangle]
49+
pub static mut H: u8 = 0;
50+
}
51+
52+
const HIDDEN: () = {
53+
// CHECK: @I = local_unnamed_addr constant
54+
#[no_mangle]
55+
static I: u8 = 0;
56+
57+
// CHECK: @J = local_unnamed_addr global
58+
#[no_mangle]
59+
static mut J: u8 = 0;
60+
61+
// CHECK: @K = local_unnamed_addr constant
62+
#[no_mangle]
63+
pub static K: u8 = 0;
64+
65+
// CHECK: @L = local_unnamed_addr global
66+
#[no_mangle]
67+
pub static mut L: u8 = 0;
68+
};
69+
70+
// The surrounding item should not accidentally become external
71+
fn x() {
72+
// CHECK: @M = local_unnamed_addr constant
73+
#[no_mangle]
74+
static M: fn() = x;
75+
76+
// CHECK: @N = local_unnamed_addr global
77+
#[no_mangle]
78+
static mut N: u8 = 0;
79+
80+
// CHECK: @O = local_unnamed_addr constant
81+
#[no_mangle]
82+
pub static O: u8 = 0;
83+
84+
// CHECK: @P = local_unnamed_addr global
85+
#[no_mangle]
86+
pub static mut P: u8 = 0;
87+
}
88+
// CHECK: define internal void @_ZN26external_no_mangle_statics1x{{.*$}}

src/test/codegen/issue-44056-macos-tls-align.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,13 @@
1717
#![crate_type = "rlib"]
1818
#![feature(thread_local)]
1919

20-
// CHECK: @STATIC_VAR_1 = internal thread_local unnamed_addr global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4
20+
// CHECK: @STATIC_VAR_1 = thread_local local_unnamed_addr global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4
2121
#[no_mangle]
22-
#[allow(private_no_mangle_statics)]
2322
#[thread_local]
2423
static mut STATIC_VAR_1: [u32; 8] = [0; 8];
2524

26-
// CHECK: @STATIC_VAR_2 = internal thread_local unnamed_addr global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4
25+
// CHECK: @STATIC_VAR_2 = thread_local local_unnamed_addr global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4
2726
#[no_mangle]
28-
#[allow(private_no_mangle_statics)]
2927
#[thread_local]
3028
static mut STATIC_VAR_2: [u32; 8] = [4; 8];
3129

0 commit comments

Comments
 (0)