| // check-pass |
| // aux-build:external_extern_fn.rs |
| #![crate_type = "lib"] |
| #![feature(no_niche)] |
| #![warn(clashing_extern_declarations)] |
| |
| mod redeclared_different_signature { |
| mod a { |
| extern "C" { |
| fn clash(x: u8); |
| } |
| } |
| mod b { |
| extern "C" { |
| fn clash(x: u64); //~ WARN `clash` redeclared with a different signature |
| } |
| } |
| } |
| |
| mod redeclared_same_signature { |
| mod a { |
| extern "C" { |
| fn no_clash(x: u8); |
| } |
| } |
| mod b { |
| extern "C" { |
| fn no_clash(x: u8); |
| } |
| } |
| } |
| |
| extern crate external_extern_fn; |
| mod extern_no_clash { |
| // Should not clash with external_extern_fn::extern_fn. |
| extern "C" { |
| fn extern_fn(x: u8); |
| } |
| } |
| |
| extern "C" { |
| fn some_other_new_name(x: i16); |
| |
| #[link_name = "extern_link_name"] |
| fn some_new_name(x: i16); |
| |
| #[link_name = "link_name_same"] |
| fn both_names_different(x: i16); |
| } |
| |
| fn link_name_clash() { |
| extern "C" { |
| fn extern_link_name(x: u32); |
| //~^ WARN `extern_link_name` redeclared with a different signature |
| |
| #[link_name = "some_other_new_name"] |
| //~^ WARN `some_other_extern_link_name` redeclares `some_other_new_name` with a different |
| fn some_other_extern_link_name(x: u32); |
| |
| #[link_name = "link_name_same"] |
| //~^ WARN `other_both_names_different` redeclares `link_name_same` with a different |
| fn other_both_names_different(x: u32); |
| } |
| } |
| |
| mod a { |
| extern "C" { |
| fn different_mod(x: u8); |
| } |
| } |
| mod b { |
| extern "C" { |
| fn different_mod(x: u64); //~ WARN `different_mod` redeclared with a different signature |
| } |
| } |
| |
| extern "C" { |
| fn variadic_decl(x: u8, ...); |
| } |
| |
| fn variadic_clash() { |
| extern "C" { |
| fn variadic_decl(x: u8); //~ WARN `variadic_decl` redeclared with a different signature |
| } |
| } |
| |
| #[no_mangle] |
| fn no_mangle_name(x: u8) {} |
| |
| extern "C" { |
| #[link_name = "unique_link_name"] |
| fn link_name_specified(x: u8); |
| } |
| |
| fn tricky_no_clash() { |
| extern "C" { |
| // Shouldn't warn, because the declaration above actually declares a different symbol (and |
| // Rust's name resolution rules around shadowing will handle this gracefully). |
| fn link_name_specified() -> u32; |
| |
| // The case of a no_mangle name colliding with an extern decl (see #28179) is related but |
| // shouldn't be reported by ClashingExternDeclarations, because this is an example of |
| // unmangled name clash causing bad behaviour in functions with a defined body. |
| fn no_mangle_name() -> u32; |
| } |
| } |
| |
| mod banana { |
| mod one { |
| #[repr(C)] |
| struct Banana { |
| weight: u32, |
| length: u16, |
| } |
| extern "C" { |
| fn weigh_banana(count: *const Banana) -> u64; |
| } |
| } |
| |
| mod two { |
| #[repr(C)] |
| struct Banana { |
| weight: u32, |
| length: u16, |
| } // note: distinct type |
| // This should not trigger the lint because two::Banana is structurally equivalent to |
| // one::Banana. |
| extern "C" { |
| fn weigh_banana(count: *const Banana) -> u64; |
| } |
| } |
| |
| mod three { |
| // This _should_ trigger the lint, because repr(packed) should generate a struct that has a |
| // different layout. |
| #[repr(packed)] |
| struct Banana { |
| weight: u32, |
| length: u16, |
| } |
| #[allow(improper_ctypes)] |
| extern "C" { |
| fn weigh_banana(count: *const Banana) -> u64; |
| //~^ WARN `weigh_banana` redeclared with a different signature |
| } |
| } |
| } |
| |
| mod sameish_members { |
| mod a { |
| #[repr(C)] |
| struct Point { |
| x: i16, |
| y: i16, |
| } |
| |
| extern "C" { |
| fn draw_point(p: Point); |
| } |
| } |
| mod b { |
| #[repr(C)] |
| struct Point { |
| coordinates: [i16; 2], |
| } |
| |
| // It's possible we are overconservative for this case, as accessing the elements of the |
| // coordinates array might end up correctly accessing `.x` and `.y`. However, this may not |
| // always be the case, for every architecture and situation. This is also a really odd |
| // thing to do anyway. |
| extern "C" { |
| fn draw_point(p: Point); |
| //~^ WARN `draw_point` redeclared with a different signature |
| } |
| } |
| } |
| |
| mod same_sized_members_clash { |
| mod a { |
| #[repr(C)] |
| struct Point3 { |
| x: f32, |
| y: f32, |
| z: f32, |
| } |
| extern "C" { |
| fn origin() -> Point3; |
| } |
| } |
| mod b { |
| #[repr(C)] |
| struct Point3 { |
| x: i32, |
| y: i32, |
| z: i32, // NOTE: Incorrectly redeclared as i32 |
| } |
| extern "C" { |
| fn origin() -> Point3; //~ WARN `origin` redeclared with a different signature |
| } |
| } |
| } |
| |
| mod transparent { |
| #[repr(transparent)] |
| struct T(usize); |
| mod a { |
| use super::T; |
| extern "C" { |
| fn transparent() -> T; |
| fn transparent_incorrect() -> T; |
| } |
| } |
| |
| mod b { |
| extern "C" { |
| // Shouldn't warn here, because repr(transparent) guarantees that T's layout is the |
| // same as just the usize. |
| fn transparent() -> usize; |
| |
| // Should warn, because there's a signedness conversion here: |
| fn transparent_incorrect() -> isize; |
| //~^ WARN `transparent_incorrect` redeclared with a different signature |
| } |
| } |
| } |
| |
| mod missing_return_type { |
| mod a { |
| extern "C" { |
| fn missing_return_type() -> usize; |
| } |
| } |
| |
| mod b { |
| extern "C" { |
| // This should output a warning because we can't assume that the first declaration is |
| // the correct one -- if this one is the correct one, then calling the usize-returning |
| // version would allow reads into uninitialised memory. |
| fn missing_return_type(); |
| //~^ WARN `missing_return_type` redeclared with a different signature |
| } |
| } |
| } |
| |
| mod non_zero_and_non_null { |
| mod a { |
| extern "C" { |
| fn non_zero_usize() -> core::num::NonZeroUsize; |
| fn non_null_ptr() -> core::ptr::NonNull<usize>; |
| } |
| } |
| mod b { |
| extern "C" { |
| // If there's a clash in either of these cases you're either gaining an incorrect |
| // invariant that the value is non-zero, or you're missing out on that invariant. Both |
| // cases are warning for, from both a caller-convenience and optimisation perspective. |
| fn non_zero_usize() -> usize; |
| //~^ WARN `non_zero_usize` redeclared with a different signature |
| fn non_null_ptr() -> *const usize; |
| //~^ WARN `non_null_ptr` redeclared with a different signature |
| } |
| } |
| } |
| |
| // See #75739 |
| mod non_zero_transparent { |
| mod a1 { |
| use std::num::NonZeroUsize; |
| extern "C" { |
| fn f1() -> NonZeroUsize; |
| } |
| } |
| |
| mod b1 { |
| #[repr(transparent)] |
| struct X(NonZeroUsize); |
| use std::num::NonZeroUsize; |
| extern "C" { |
| fn f1() -> X; |
| } |
| } |
| |
| mod a2 { |
| use std::num::NonZeroUsize; |
| extern "C" { |
| fn f2() -> NonZeroUsize; |
| } |
| } |
| |
| mod b2 { |
| #[repr(transparent)] |
| struct X1(NonZeroUsize); |
| |
| #[repr(transparent)] |
| struct X(X1); |
| |
| use std::num::NonZeroUsize; |
| extern "C" { |
| // Same case as above, but with two layers of newtyping. |
| fn f2() -> X; |
| } |
| } |
| |
| mod a3 { |
| #[repr(transparent)] |
| struct X(core::ptr::NonNull<i32>); |
| |
| use std::num::NonZeroUsize; |
| extern "C" { |
| fn f3() -> X; |
| } |
| } |
| |
| mod b3 { |
| extern "C" { |
| fn f3() -> core::ptr::NonNull<i32>; |
| } |
| } |
| |
| mod a4 { |
| #[repr(transparent)] |
| enum E { |
| X(std::num::NonZeroUsize), |
| } |
| extern "C" { |
| fn f4() -> E; |
| } |
| } |
| |
| mod b4 { |
| extern "C" { |
| fn f4() -> std::num::NonZeroUsize; |
| } |
| } |
| } |
| |
| mod null_optimised_enums { |
| mod a { |
| extern "C" { |
| fn option_non_zero_usize() -> usize; |
| fn option_non_zero_isize() -> isize; |
| fn option_non_null_ptr() -> *const usize; |
| |
| fn option_non_zero_usize_incorrect() -> usize; |
| fn option_non_null_ptr_incorrect() -> *const usize; |
| } |
| } |
| mod b { |
| extern "C" { |
| // This should be allowed, because these conversions are guaranteed to be FFI-safe (see |
| // #60300) |
| fn option_non_zero_usize() -> Option<core::num::NonZeroUsize>; |
| fn option_non_zero_isize() -> Option<core::num::NonZeroIsize>; |
| fn option_non_null_ptr() -> Option<core::ptr::NonNull<usize>>; |
| |
| // However, these should be incorrect (note isize instead of usize) |
| fn option_non_zero_usize_incorrect() -> isize; |
| //~^ WARN `option_non_zero_usize_incorrect` redeclared with a different signature |
| fn option_non_null_ptr_incorrect() -> *const isize; |
| //~^ WARN `option_non_null_ptr_incorrect` redeclared with a different signature |
| } |
| } |
| } |
| |
| #[allow(improper_ctypes)] |
| mod unknown_layout { |
| mod a { |
| extern "C" { |
| pub fn generic(l: Link<u32>); |
| } |
| pub struct Link<T> { |
| pub item: T, |
| pub next: *const Link<T>, |
| } |
| } |
| |
| mod b { |
| extern "C" { |
| pub fn generic(l: Link<u32>); |
| } |
| pub struct Link<T> { |
| pub item: T, |
| pub next: *const Link<T>, |
| } |
| } |
| } |
| |
| mod hidden_niche { |
| mod a { |
| extern "C" { |
| fn hidden_niche_transparent() -> usize; |
| fn hidden_niche_transparent_no_niche() -> usize; |
| fn hidden_niche_unsafe_cell() -> usize; |
| } |
| } |
| mod b { |
| use std::cell::UnsafeCell; |
| use std::num::NonZeroUsize; |
| |
| #[repr(transparent)] |
| struct Transparent { x: NonZeroUsize } |
| |
| #[repr(no_niche)] |
| #[repr(transparent)] |
| struct TransparentNoNiche { y: NonZeroUsize } |
| |
| extern "C" { |
| fn hidden_niche_transparent() -> Option<Transparent>; |
| |
| fn hidden_niche_transparent_no_niche() -> Option<TransparentNoNiche>; |
| //~^ WARN redeclared with a different signature |
| //~| WARN block uses type `Option<TransparentNoNiche>`, which is not FFI-safe |
| |
| fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZeroUsize>>; |
| //~^ WARN redeclared with a different signature |
| //~| WARN block uses type `Option<UnsafeCell<NonZeroUsize>>`, which is not FFI-safe |
| } |
| } |
| } |