diff options
Diffstat (limited to '')
-rw-r--r-- | src/test/ui/lint/clashing-extern-fn.rs | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/src/test/ui/lint/clashing-extern-fn.rs b/src/test/ui/lint/clashing-extern-fn.rs new file mode 100644 index 000000000..809e06026 --- /dev/null +++ b/src/test/ui/lint/clashing-extern-fn.rs @@ -0,0 +1,417 @@ +// check-pass +// aux-build:external_extern_fn.rs +#![crate_type = "lib"] +#![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(transparent)] + struct TransparentNoNiche { y: UnsafeCell<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 + } + } +} |