Skip to content

Commit 5a19ffe

Browse files
committed
Auto merge of #86492 - hyd-dev:no-mangle-method, r=petrochenkov
Associated functions that contain extern indicator or have `#[rustc_std_internal_symbol]` are reachable Previously these fails to link with ``undefined reference to `foo'``: <details> <summary>Example 1</summary> ```rs struct AssocFn; impl AssocFn { #[no_mangle] fn foo() {} } fn main() { extern "Rust" { fn foo(); } unsafe { foo() } } ``` ([Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=f1244afcdd26e2a28445f6e82ca46b50)) </details> <details> <summary>Example 2</summary> ```rs #![crate_name = "lib"] #![crate_type = "lib"] struct AssocFn; impl AssocFn { #[no_mangle] fn foo() {} } ``` ```rs extern crate lib; fn main() { extern "Rust" { fn foo(); } unsafe { foo() } } ``` </details> But I believe they should link successfully, because this works: <details> ```rs #[no_mangle] fn foo() {} fn main() { extern "Rust" { fn foo(); } unsafe { foo() } } ``` ([Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=789b3f283ee6126f53939429103ed98d)) </details> This PR fixes the problem, by adding associated functions that have "custom linkage" to `reachable_set`, just like normal functions. I haven't tested whether #76211 and [Miri](rust-lang/miri#1837) are fixed by this PR yet, but I'm submitting this anyway since this fixes the examples above. I added a `run-pass` test that combines my two examples above, but I'm not sure if that's the right way to test this. Maybe I should add / modify an existing codegen test (`src/test/codegen/export-no-mangle.rs`?) instead?
2 parents 881aeab + 9315a0c commit 5a19ffe

19 files changed

+920
-251
lines changed

compiler/rustc_ast_passes/src/ast_validation.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14991499
}
15001500

15011501
fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
1502+
if self.session.contains_name(&item.attrs, sym::no_mangle) {
1503+
self.check_nomangle_item_asciionly(item.ident, item.span);
1504+
}
1505+
15021506
if ctxt == AssocCtxt::Trait || !self.in_trait_impl {
15031507
self.check_defaultness(item.span, item.kind.defaultness());
15041508
}

compiler/rustc_lint/src/builtin.rs

+64-22
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,25 @@ impl EarlyLintPass for UnsafeCode {
417417
}
418418
}
419419

420+
fn check_impl_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) {
421+
if let ast::AssocItemKind::Fn(..) = it.kind {
422+
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
423+
self.report_overriden_symbol_name(
424+
cx,
425+
attr.span,
426+
"declaration of a `no_mangle` method",
427+
);
428+
}
429+
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
430+
self.report_overriden_symbol_name(
431+
cx,
432+
attr.span,
433+
"declaration of a method with `export_name`",
434+
);
435+
}
436+
}
437+
}
438+
420439
fn check_fn(&mut self, cx: &EarlyContext<'_>, fk: FnKind<'_>, span: Span, _: ast::NodeId) {
421440
if let FnKind::Fn(
422441
ctxt,
@@ -1115,31 +1134,37 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN
11151134
impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
11161135
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
11171136
let attrs = cx.tcx.hir().attrs(it.hir_id());
1137+
let check_no_mangle_on_generic_fn = |no_mangle_attr: &ast::Attribute,
1138+
impl_generics: Option<&hir::Generics<'_>>,
1139+
generics: &hir::Generics<'_>,
1140+
span| {
1141+
for param in
1142+
generics.params.iter().chain(impl_generics.map(|g| g.params).into_iter().flatten())
1143+
{
1144+
match param.kind {
1145+
GenericParamKind::Lifetime { .. } => {}
1146+
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
1147+
cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, span, |lint| {
1148+
lint.build("functions generic over types or consts must be mangled")
1149+
.span_suggestion_short(
1150+
no_mangle_attr.span,
1151+
"remove this attribute",
1152+
String::new(),
1153+
// Use of `#[no_mangle]` suggests FFI intent; correct
1154+
// fix may be to monomorphize source by hand
1155+
Applicability::MaybeIncorrect,
1156+
)
1157+
.emit();
1158+
});
1159+
break;
1160+
}
1161+
}
1162+
}
1163+
};
11181164
match it.kind {
11191165
hir::ItemKind::Fn(.., ref generics, _) => {
11201166
if let Some(no_mangle_attr) = cx.sess().find_by_name(attrs, sym::no_mangle) {
1121-
for param in generics.params {
1122-
match param.kind {
1123-
GenericParamKind::Lifetime { .. } => {}
1124-
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
1125-
cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, it.span, |lint| {
1126-
lint.build(
1127-
"functions generic over types or consts must be mangled",
1128-
)
1129-
.span_suggestion_short(
1130-
no_mangle_attr.span,
1131-
"remove this attribute",
1132-
String::new(),
1133-
// Use of `#[no_mangle]` suggests FFI intent; correct
1134-
// fix may be to monomorphize source by hand
1135-
Applicability::MaybeIncorrect,
1136-
)
1137-
.emit();
1138-
});
1139-
break;
1140-
}
1141-
}
1142-
}
1167+
check_no_mangle_on_generic_fn(no_mangle_attr, None, generics, it.span);
11431168
}
11441169
}
11451170
hir::ItemKind::Const(..) => {
@@ -1170,6 +1195,23 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
11701195
});
11711196
}
11721197
}
1198+
hir::ItemKind::Impl(hir::Impl { ref generics, items, .. }) => {
1199+
for it in items {
1200+
if let hir::AssocItemKind::Fn { .. } = it.kind {
1201+
if let Some(no_mangle_attr) = cx
1202+
.sess()
1203+
.find_by_name(cx.tcx.hir().attrs(it.id.hir_id()), sym::no_mangle)
1204+
{
1205+
check_no_mangle_on_generic_fn(
1206+
no_mangle_attr,
1207+
Some(generics),
1208+
cx.tcx.hir().get_generics(it.id.def_id.to_def_id()).unwrap(),
1209+
it.span,
1210+
);
1211+
}
1212+
}
1213+
}
1214+
}
11731215
_ => {}
11741216
}
11751217
}

compiler/rustc_lint/src/nonstandard_style.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -391,9 +391,14 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
391391
_: Span,
392392
id: hir::HirId,
393393
) {
394+
let attrs = cx.tcx.hir().attrs(id);
394395
match &fk {
395-
FnKind::Method(ident, ..) => match method_context(cx, id) {
396+
FnKind::Method(ident, sig, ..) => match method_context(cx, id) {
396397
MethodLateContext::PlainImpl => {
398+
if sig.header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle)
399+
{
400+
return;
401+
}
397402
self.check_snake_case(cx, "method", ident);
398403
}
399404
MethodLateContext::TraitAutoImpl => {
@@ -402,7 +407,6 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
402407
_ => (),
403408
},
404409
FnKind::ItemFn(ident, _, header, _) => {
405-
let attrs = cx.tcx.hir().attrs(id);
406410
// Skip foreign-ABI #[no_mangle] functions (Issue #31924)
407411
if header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle) {
408412
return;

compiler/rustc_passes/src/check_attr.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,10 @@ impl CheckAttrVisitor<'tcx> {
962962
}
963963
}
964964

965+
fn is_impl_item(&self, hir_id: HirId) -> bool {
966+
matches!(self.tcx.hir().get(hir_id), hir::Node::ImplItem(..))
967+
}
968+
965969
/// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
966970
fn check_export_name(
967971
&self,
@@ -971,7 +975,8 @@ impl CheckAttrVisitor<'tcx> {
971975
target: Target,
972976
) -> bool {
973977
match target {
974-
Target::Static | Target::Fn | Target::Method(..) => true,
978+
Target::Static | Target::Fn => true,
979+
Target::Method(..) if self.is_impl_item(hir_id) => true,
975980
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
976981
// `#[export_name]` attribute with just a lint, because we previously
977982
// erroneously allowed it and some crates used it accidentally, to to be compatible
@@ -985,9 +990,9 @@ impl CheckAttrVisitor<'tcx> {
985990
.sess
986991
.struct_span_err(
987992
attr.span,
988-
"attribute should be applied to a function or static",
993+
"attribute should be applied to a free function, impl method or static",
989994
)
990-
.span_label(*span, "not a function or static")
995+
.span_label(*span, "not a free function, impl method or static")
991996
.emit();
992997
false
993998
}
@@ -1169,7 +1174,8 @@ impl CheckAttrVisitor<'tcx> {
11691174
/// Checks if `#[no_mangle]` is applied to a function or static.
11701175
fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
11711176
match target {
1172-
Target::Static | Target::Fn | Target::Method(..) => {}
1177+
Target::Static | Target::Fn => {}
1178+
Target::Method(..) if self.is_impl_item(hir_id) => {}
11731179
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
11741180
// `#[no_mangle]` attribute with just a lint, because we previously
11751181
// erroneously allowed it and some crates used it accidentally, to to be compatible
@@ -1181,14 +1187,16 @@ impl CheckAttrVisitor<'tcx> {
11811187
// FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
11821188
// crates used this, so only emit a warning.
11831189
self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1184-
lint.build("attribute should be applied to a function or static")
1185-
.warn(
1186-
"this was previously accepted by the compiler but is \
1187-
being phased out; it will become a hard error in \
1188-
a future release!",
1189-
)
1190-
.span_label(*span, "not a function or static")
1191-
.emit();
1190+
lint.build(
1191+
"attribute should be applied to a free function, impl method or static",
1192+
)
1193+
.warn(
1194+
"this was previously accepted by the compiler but is \
1195+
being phased out; it will become a hard error in \
1196+
a future release!",
1197+
)
1198+
.span_label(*span, "not a free function, impl method or static")
1199+
.emit();
11921200
});
11931201
}
11941202
}

compiler/rustc_passes/src/reachable.rs

+21-13
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,15 @@ impl<'tcx> ReachableContext<'tcx> {
211211
if !self.any_library {
212212
// If we are building an executable, only explicitly extern
213213
// types need to be exported.
214-
if let Node::Item(item) = *node {
215-
let reachable = if let hir::ItemKind::Fn(ref sig, ..) = item.kind {
216-
sig.header.abi != Abi::Rust
217-
} else {
218-
false
219-
};
220-
let codegen_attrs = self.tcx.codegen_fn_attrs(item.def_id);
214+
if let Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), def_id, .. })
215+
| Node::ImplItem(hir::ImplItem {
216+
kind: hir::ImplItemKind::Fn(sig, ..),
217+
def_id,
218+
..
219+
}) = *node
220+
{
221+
let reachable = sig.header.abi != Abi::Rust;
222+
let codegen_attrs = self.tcx.codegen_fn_attrs(*def_id);
221223
let is_extern = codegen_attrs.contains_extern_indicator();
222224
let std_internal =
223225
codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
@@ -335,17 +337,23 @@ struct CollectPrivateImplItemsVisitor<'a, 'tcx> {
335337
worklist: &'a mut Vec<LocalDefId>,
336338
}
337339

338-
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx> {
339-
fn visit_item(&mut self, item: &hir::Item<'_>) {
340+
impl CollectPrivateImplItemsVisitor<'_, '_> {
341+
fn push_to_worklist_if_has_custom_linkage(&mut self, def_id: LocalDefId) {
340342
// Anything which has custom linkage gets thrown on the worklist no
341343
// matter where it is in the crate, along with "special std symbols"
342344
// which are currently akin to allocator symbols.
343-
let codegen_attrs = self.tcx.codegen_fn_attrs(item.def_id);
345+
let codegen_attrs = self.tcx.codegen_fn_attrs(def_id);
344346
if codegen_attrs.contains_extern_indicator()
345347
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
346348
{
347-
self.worklist.push(item.def_id);
349+
self.worklist.push(def_id);
348350
}
351+
}
352+
}
353+
354+
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx> {
355+
fn visit_item(&mut self, item: &hir::Item<'_>) {
356+
self.push_to_worklist_if_has_custom_linkage(item.def_id);
349357

350358
// We need only trait impls here, not inherent impls, and only non-exported ones
351359
if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) =
@@ -375,8 +383,8 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx
375383

376384
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
377385

378-
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {
379-
// processed in visit_item above
386+
fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) {
387+
self.push_to_worklist_if_has_custom_linkage(impl_item.def_id);
380388
}
381389

382390
fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![crate_type = "lib"]
2+
3+
struct Bar;
4+
5+
impl Bar {
6+
#[no_mangle]
7+
fn bar() -> u8 {
8+
2
9+
}
10+
}
11+
12+
trait Foo {
13+
fn baz() -> u8;
14+
}
15+
16+
impl Foo for Bar {
17+
#[no_mangle]
18+
fn baz() -> u8 {
19+
3
20+
}
21+
}

src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs

+22-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//~ NOTE: not an `extern crate` item
2-
//~^ NOTE: not a function or static
2+
//~^ NOTE: not a free function, impl method or static
33
//~^^ NOTE: not a function or closure
44
// This is testing whether various builtin attributes signals an
55
// error or warning when put in "weird" places.
@@ -25,7 +25,7 @@
2525
#![no_link]
2626
//~^ ERROR: attribute should be applied to an `extern crate` item
2727
#![export_name = "2200"]
28-
//~^ ERROR: attribute should be applied to a function or static
28+
//~^ ERROR: attribute should be applied to a free function, impl method or static
2929
#![inline]
3030
//~^ ERROR: attribute should be applied to function or closure
3131
#[inline]
@@ -83,27 +83,37 @@ mod no_link {
8383
}
8484

8585
#[export_name = "2200"]
86-
//~^ ERROR attribute should be applied to a function or static
86+
//~^ ERROR attribute should be applied to a free function, impl method or static
8787
mod export_name {
88-
//~^ NOTE not a function or static
88+
//~^ NOTE not a free function, impl method or static
8989

9090
mod inner { #![export_name="2200"] }
91-
//~^ ERROR attribute should be applied to a function or static
92-
//~| NOTE not a function or static
91+
//~^ ERROR attribute should be applied to a free function, impl method or static
92+
//~| NOTE not a free function, impl method or static
9393

9494
#[export_name = "2200"] fn f() { }
9595

9696
#[export_name = "2200"] struct S;
97-
//~^ ERROR attribute should be applied to a function or static
98-
//~| NOTE not a function or static
97+
//~^ ERROR attribute should be applied to a free function, impl method or static
98+
//~| NOTE not a free function, impl method or static
9999

100100
#[export_name = "2200"] type T = S;
101-
//~^ ERROR attribute should be applied to a function or static
102-
//~| NOTE not a function or static
101+
//~^ ERROR attribute should be applied to a free function, impl method or static
102+
//~| NOTE not a free function, impl method or static
103103

104104
#[export_name = "2200"] impl S { }
105-
//~^ ERROR attribute should be applied to a function or static
106-
//~| NOTE not a function or static
105+
//~^ ERROR attribute should be applied to a free function, impl method or static
106+
//~| NOTE not a free function, impl method or static
107+
108+
trait Tr {
109+
#[export_name = "2200"] fn foo();
110+
//~^ ERROR attribute should be applied to a free function, impl method or static
111+
//~| NOTE not a free function, impl method or static
112+
113+
#[export_name = "2200"] fn bar() {}
114+
//~^ ERROR attribute should be applied to a free function, impl method or static
115+
//~| NOTE not a free function, impl method or static
116+
}
107117
}
108118

109119
#[start]

0 commit comments

Comments
 (0)