Skip to content

Warn when #[export_name] is used with generic functions #140851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,17 +976,21 @@ declare_lint! {
/// ```rust
/// #[unsafe(no_mangle)]
/// fn foo<T>(t: T) {}
///
/// #[unsafe(export_name = "bar")]
/// fn bar<T>(t: T) {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// A function with generics must have its symbol mangled to accommodate
/// the generic parameter. The [`no_mangle` attribute] has no effect in
/// this situation, and should be removed.
/// the generic parameter. The [`no_mangle`] and [`export_name`] attributes
/// have no effect in this situation, and should be removed.
///
/// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute
/// [`no_mangle`]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute
/// [`export_name`]: https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute
NO_MANGLE_GENERIC_ITEMS,
Warn,
"generic items must be mangled"
Expand All @@ -997,7 +1001,7 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN
impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
let attrs = cx.tcx.hir_attrs(it.hir_id());
let check_no_mangle_on_generic_fn = |no_mangle_attr: &hir::Attribute,
let check_no_mangle_on_generic_fn = |attr: &hir::Attribute,
impl_generics: Option<&hir::Generics<'_>>,
generics: &hir::Generics<'_>,
span| {
Expand All @@ -1010,7 +1014,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
cx.emit_span_lint(
NO_MANGLE_GENERIC_ITEMS,
span,
BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span() },
BuiltinNoMangleGeneric { suggestion: attr.span() },
);
break;
}
Expand All @@ -1019,8 +1023,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
};
match it.kind {
hir::ItemKind::Fn { generics, .. } => {
if let Some(no_mangle_attr) = attr::find_by_name(attrs, sym::no_mangle) {
check_no_mangle_on_generic_fn(no_mangle_attr, None, generics, it.span);
if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
.or_else(|| attr::find_by_name(attrs, sym::no_mangle))
{
check_no_mangle_on_generic_fn(attr, None, generics, it.span);
}
}
hir::ItemKind::Const(..) => {
Expand Down Expand Up @@ -1048,11 +1054,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => {
for it in *items {
if let hir::AssocItemKind::Fn { .. } = it.kind {
if let Some(no_mangle_attr) =
attr::find_by_name(cx.tcx.hir_attrs(it.id.hir_id()), sym::no_mangle)
let attrs = cx.tcx.hir_attrs(it.id.hir_id());
if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
.or_else(|| attr::find_by_name(attrs, sym::no_mangle))
{
check_no_mangle_on_generic_fn(
no_mangle_attr,
attr,
Some(generics),
cx.tcx.hir_get_generics(it.id.owner_id.def_id).unwrap(),
it.span,
Expand Down
157 changes: 157 additions & 0 deletions tests/ui/generics/export-name-on-generics.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//@ run-rustfix
#![allow(dead_code, elided_named_lifetimes)]
#![deny(no_mangle_generic_items)]

pub fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled

pub extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
pub fn baz(x: &i32) -> &i32 { x }

#[export_name = "qux"]
pub fn qux<'a>(x: &'a i32) -> &i32 { x }

pub struct Foo;

impl Foo {

pub fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled


pub extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
pub fn baz(x: &i32) -> &i32 { x }

#[export_name = "qux"]
pub fn qux<'a>(x: &'a i32) -> &i32 { x }
}

trait Trait1 {
fn foo<T>();
extern "C" fn bar<T>();
fn baz(x: &i32) -> &i32;
fn qux<'a>(x: &'a i32) -> &i32;
}

impl Trait1 for Foo {

fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled


extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
fn baz(x: &i32) -> &i32 { x }

#[export_name = "qux"]
fn qux<'a>(x: &'a i32) -> &i32 { x }
}

trait Trait2<T> {
fn foo();
fn foo2<U>();
extern "C" fn bar();
fn baz(x: &i32) -> &i32;
fn qux<'a>(x: &'a i32) -> &i32;
}

impl<T> Trait2<T> for Foo {

fn foo() {} //~ ERROR functions generic over types or consts must be mangled


fn foo2<U>() {} //~ ERROR functions generic over types or consts must be mangled


extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled


fn baz(x: &i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled


fn qux<'a>(x: &'a i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled
}

pub struct Bar<T>(#[allow(dead_code)] T);

impl<T> Bar<T> {

pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled


pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled


pub fn baz<U>() {} //~ ERROR functions generic over types or consts must be mangled
}

impl Bar<i32> {
#[export_name = "qux"]
pub fn qux() {}
}

trait Trait3 {
fn foo();
extern "C" fn bar();
fn baz<U>();
}

impl<T> Trait3 for Bar<T> {

fn foo() {} //~ ERROR functions generic over types or consts must be mangled


extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled


fn baz<U>() {} //~ ERROR functions generic over types or consts must be mangled
}

pub struct Baz<'a>(#[allow(dead_code)] &'a i32);

impl<'a> Baz<'a> {
#[export_name = "foo"]
pub fn foo() {}

#[export_name = "bar"]
pub fn bar<'b>(x: &'b i32) -> &i32 { x }
}

trait Trait4 {
fn foo();
fn bar<'a>(x: &'a i32) -> &i32;
}

impl Trait4 for Bar<i32> {
#[export_name = "foo"]
fn foo() {}

#[export_name = "bar"]
fn bar<'b>(x: &'b i32) -> &i32 { x }
}

impl<'a> Trait4 for Baz<'a> {
#[export_name = "foo"]
fn foo() {}

#[export_name = "bar"]
fn bar<'b>(x: &'b i32) -> &i32 { x }
}

trait Trait5<T> {
fn foo();
}

impl Trait5<i32> for Foo {
#[export_name = "foo"]
fn foo() {}
}

impl Trait5<i32> for Bar<i32> {
#[export_name = "foo"]
fn foo() {}
}

fn main() {}
159 changes: 159 additions & 0 deletions tests/ui/generics/export-name-on-generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//@ run-rustfix
#![allow(dead_code, elided_named_lifetimes)]
#![deny(no_mangle_generic_items)]

#[export_name = "foo"]
pub fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "bar"]
pub extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
pub fn baz(x: &i32) -> &i32 { x }

#[export_name = "qux"]
pub fn qux<'a>(x: &'a i32) -> &i32 { x }

pub struct Foo;

impl Foo {
#[export_name = "foo"]
pub fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "bar"]
pub extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
pub fn baz(x: &i32) -> &i32 { x }

#[export_name = "qux"]
pub fn qux<'a>(x: &'a i32) -> &i32 { x }
}

trait Trait1 {
fn foo<T>();
extern "C" fn bar<T>();
fn baz(x: &i32) -> &i32;
fn qux<'a>(x: &'a i32) -> &i32;
}

impl Trait1 for Foo {
#[export_name = "foo"]
fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "bar"]
extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
fn baz(x: &i32) -> &i32 { x }

#[export_name = "qux"]
fn qux<'a>(x: &'a i32) -> &i32 { x }
}

trait Trait2<T> {
fn foo();
fn foo2<U>();
extern "C" fn bar();
fn baz(x: &i32) -> &i32;
fn qux<'a>(x: &'a i32) -> &i32;
}

impl<T> Trait2<T> for Foo {
#[export_name = "foo"]
fn foo() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "foo2"]
fn foo2<U>() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
fn baz(x: &i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled

#[export_name = "qux"]
fn qux<'a>(x: &'a i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled
}

pub struct Bar<T>(#[allow(dead_code)] T);

impl<T> Bar<T> {
#[export_name = "foo"]
pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "bar"]
pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
pub fn baz<U>() {} //~ ERROR functions generic over types or consts must be mangled
}

impl Bar<i32> {
#[export_name = "qux"]
pub fn qux() {}
}

trait Trait3 {
fn foo();
extern "C" fn bar();
fn baz<U>();
}

impl<T> Trait3 for Bar<T> {
#[export_name = "foo"]
fn foo() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "bar"]
extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled

#[export_name = "baz"]
fn baz<U>() {} //~ ERROR functions generic over types or consts must be mangled
}

pub struct Baz<'a>(#[allow(dead_code)] &'a i32);

impl<'a> Baz<'a> {
#[export_name = "foo"]
pub fn foo() {}

#[export_name = "bar"]
pub fn bar<'b>(x: &'b i32) -> &i32 { x }
}

trait Trait4 {
fn foo();
fn bar<'a>(x: &'a i32) -> &i32;
}

impl Trait4 for Bar<i32> {
#[export_name = "foo"]
fn foo() {}

#[export_name = "bar"]
fn bar<'b>(x: &'b i32) -> &i32 { x }
}

impl<'a> Trait4 for Baz<'a> {
#[export_name = "foo"]
fn foo() {}

#[export_name = "bar"]
fn bar<'b>(x: &'b i32) -> &i32 { x }
}

trait Trait5<T> {
fn foo();
}

impl Trait5<i32> for Foo {
#[export_name = "foo"]
fn foo() {}
}

impl Trait5<i32> for Bar<i32> {
#[export_name = "foo"]
fn foo() {}
}

fn main() {}
Loading
Loading