]> git.lizzy.rs Git - rust.git/blob - tests/ui/lint/clashing-extern-fn.rs
Auto merge of #106884 - clubby789:fieldless-enum-debug, r=michaelwoerister
[rust.git] / tests / ui / lint / clashing-extern-fn.rs
1 // check-pass
2 // aux-build:external_extern_fn.rs
3 #![crate_type = "lib"]
4 #![warn(clashing_extern_declarations)]
5
6 mod redeclared_different_signature {
7     mod a {
8         extern "C" {
9             fn clash(x: u8);
10         }
11     }
12     mod b {
13         extern "C" {
14             fn clash(x: u64); //~ WARN `clash` redeclared with a different signature
15         }
16     }
17 }
18
19 mod redeclared_same_signature {
20     mod a {
21         extern "C" {
22             fn no_clash(x: u8);
23         }
24     }
25     mod b {
26         extern "C" {
27             fn no_clash(x: u8);
28         }
29     }
30 }
31
32 extern crate external_extern_fn;
33 mod extern_no_clash {
34     // Should not clash with external_extern_fn::extern_fn.
35     extern "C" {
36         fn extern_fn(x: u8);
37     }
38 }
39
40 extern "C" {
41     fn some_other_new_name(x: i16);
42
43     #[link_name = "extern_link_name"]
44     fn some_new_name(x: i16);
45
46     #[link_name = "link_name_same"]
47     fn both_names_different(x: i16);
48 }
49
50 fn link_name_clash() {
51     extern "C" {
52         fn extern_link_name(x: u32);
53         //~^ WARN `extern_link_name` redeclared with a different signature
54
55         #[link_name = "some_other_new_name"]
56         //~^ WARN `some_other_extern_link_name` redeclares `some_other_new_name` with a different
57         fn some_other_extern_link_name(x: u32);
58
59         #[link_name = "link_name_same"]
60         //~^ WARN `other_both_names_different` redeclares `link_name_same` with a different
61         fn other_both_names_different(x: u32);
62     }
63 }
64
65 mod a {
66     extern "C" {
67         fn different_mod(x: u8);
68     }
69 }
70 mod b {
71     extern "C" {
72         fn different_mod(x: u64); //~ WARN `different_mod` redeclared with a different signature
73     }
74 }
75
76 extern "C" {
77     fn variadic_decl(x: u8, ...);
78 }
79
80 fn variadic_clash() {
81     extern "C" {
82         fn variadic_decl(x: u8); //~ WARN `variadic_decl` redeclared with a different signature
83     }
84 }
85
86 #[no_mangle]
87 fn no_mangle_name(x: u8) {}
88
89 extern "C" {
90     #[link_name = "unique_link_name"]
91     fn link_name_specified(x: u8);
92 }
93
94 fn tricky_no_clash() {
95     extern "C" {
96         // Shouldn't warn, because the declaration above actually declares a different symbol (and
97         // Rust's name resolution rules around shadowing will handle this gracefully).
98         fn link_name_specified() -> u32;
99
100         // The case of a no_mangle name colliding with an extern decl (see #28179) is related but
101         // shouldn't be reported by ClashingExternDeclarations, because this is an example of
102         // unmangled name clash causing bad behaviour in functions with a defined body.
103         fn no_mangle_name() -> u32;
104     }
105 }
106
107 mod banana {
108     mod one {
109         #[repr(C)]
110         struct Banana {
111             weight: u32,
112             length: u16,
113         }
114         extern "C" {
115             fn weigh_banana(count: *const Banana) -> u64;
116         }
117     }
118
119     mod two {
120         #[repr(C)]
121         struct Banana {
122             weight: u32,
123             length: u16,
124         } // note: distinct type
125           // This should not trigger the lint because two::Banana is structurally equivalent to
126           // one::Banana.
127         extern "C" {
128             fn weigh_banana(count: *const Banana) -> u64;
129         }
130     }
131
132     mod three {
133         // This _should_ trigger the lint, because repr(packed) should generate a struct that has a
134         // different layout.
135         #[repr(packed)]
136         struct Banana {
137             weight: u32,
138             length: u16,
139         }
140         #[allow(improper_ctypes)]
141         extern "C" {
142             fn weigh_banana(count: *const Banana) -> u64;
143             //~^ WARN `weigh_banana` redeclared with a different signature
144         }
145     }
146 }
147
148 mod sameish_members {
149     mod a {
150         #[repr(C)]
151         struct Point {
152             x: i16,
153             y: i16,
154         }
155
156         extern "C" {
157             fn draw_point(p: Point);
158         }
159     }
160     mod b {
161         #[repr(C)]
162         struct Point {
163             coordinates: [i16; 2],
164         }
165
166         // It's possible we are overconservative for this case, as accessing the elements of the
167         // coordinates array might end up correctly accessing `.x` and `.y`. However, this may not
168         // always be the case, for every architecture and situation. This is also a really odd
169         // thing to do anyway.
170         extern "C" {
171             fn draw_point(p: Point);
172             //~^ WARN `draw_point` redeclared with a different signature
173         }
174     }
175 }
176
177 mod same_sized_members_clash {
178     mod a {
179         #[repr(C)]
180         struct Point3 {
181             x: f32,
182             y: f32,
183             z: f32,
184         }
185         extern "C" {
186             fn origin() -> Point3;
187         }
188     }
189     mod b {
190         #[repr(C)]
191         struct Point3 {
192             x: i32,
193             y: i32,
194             z: i32, // NOTE: Incorrectly redeclared as i32
195         }
196         extern "C" {
197             fn origin() -> Point3; //~ WARN `origin` redeclared with a different signature
198         }
199     }
200 }
201
202 mod transparent {
203     #[repr(transparent)]
204     struct T(usize);
205     mod a {
206         use super::T;
207         extern "C" {
208             fn transparent() -> T;
209             fn transparent_incorrect() -> T;
210         }
211     }
212
213     mod b {
214         extern "C" {
215             // Shouldn't warn here, because repr(transparent) guarantees that T's layout is the
216             // same as just the usize.
217             fn transparent() -> usize;
218
219             // Should warn, because there's a signedness conversion here:
220             fn transparent_incorrect() -> isize;
221             //~^ WARN `transparent_incorrect` redeclared with a different signature
222         }
223     }
224 }
225
226 mod missing_return_type {
227     mod a {
228         extern "C" {
229             fn missing_return_type() -> usize;
230         }
231     }
232
233     mod b {
234         extern "C" {
235             // This should output a warning because we can't assume that the first declaration is
236             // the correct one -- if this one is the correct one, then calling the usize-returning
237             // version would allow reads into uninitialised memory.
238             fn missing_return_type();
239             //~^ WARN `missing_return_type` redeclared with a different signature
240         }
241     }
242 }
243
244 mod non_zero_and_non_null {
245     mod a {
246         extern "C" {
247             fn non_zero_usize() -> core::num::NonZeroUsize;
248             fn non_null_ptr() -> core::ptr::NonNull<usize>;
249         }
250     }
251     mod b {
252         extern "C" {
253             // If there's a clash in either of these cases you're either gaining an incorrect
254             // invariant that the value is non-zero, or you're missing out on that invariant. Both
255             // cases are warning for, from both a caller-convenience and optimisation perspective.
256             fn non_zero_usize() -> usize;
257             //~^ WARN `non_zero_usize` redeclared with a different signature
258             fn non_null_ptr() -> *const usize;
259             //~^ WARN `non_null_ptr` redeclared with a different signature
260         }
261     }
262 }
263
264 // See #75739
265 mod non_zero_transparent {
266     mod a1 {
267         use std::num::NonZeroUsize;
268         extern "C" {
269             fn f1() -> NonZeroUsize;
270         }
271     }
272
273     mod b1 {
274         #[repr(transparent)]
275         struct X(NonZeroUsize);
276         use std::num::NonZeroUsize;
277         extern "C" {
278             fn f1() -> X;
279         }
280     }
281
282     mod a2 {
283         use std::num::NonZeroUsize;
284         extern "C" {
285             fn f2() -> NonZeroUsize;
286         }
287     }
288
289     mod b2 {
290         #[repr(transparent)]
291         struct X1(NonZeroUsize);
292
293         #[repr(transparent)]
294         struct X(X1);
295
296         use std::num::NonZeroUsize;
297         extern "C" {
298             // Same case as above, but with two layers of newtyping.
299             fn f2() -> X;
300         }
301     }
302
303     mod a3 {
304         #[repr(transparent)]
305         struct X(core::ptr::NonNull<i32>);
306
307         use std::num::NonZeroUsize;
308         extern "C" {
309             fn f3() -> X;
310         }
311     }
312
313     mod b3 {
314         extern "C" {
315             fn f3() -> core::ptr::NonNull<i32>;
316         }
317     }
318
319     mod a4 {
320         #[repr(transparent)]
321         enum E {
322             X(std::num::NonZeroUsize),
323         }
324         extern "C" {
325             fn f4() -> E;
326         }
327     }
328
329     mod b4 {
330         extern "C" {
331             fn f4() -> std::num::NonZeroUsize;
332         }
333     }
334 }
335
336 mod null_optimised_enums {
337     mod a {
338         extern "C" {
339             fn option_non_zero_usize() -> usize;
340             fn option_non_zero_isize() -> isize;
341             fn option_non_null_ptr() -> *const usize;
342
343             fn option_non_zero_usize_incorrect() -> usize;
344             fn option_non_null_ptr_incorrect() -> *const usize;
345         }
346     }
347     mod b {
348         extern "C" {
349             // This should be allowed, because these conversions are guaranteed to be FFI-safe (see
350             // #60300)
351             fn option_non_zero_usize() -> Option<core::num::NonZeroUsize>;
352             fn option_non_zero_isize() -> Option<core::num::NonZeroIsize>;
353             fn option_non_null_ptr() -> Option<core::ptr::NonNull<usize>>;
354
355             // However, these should be incorrect (note isize instead of usize)
356             fn option_non_zero_usize_incorrect() -> isize;
357             //~^ WARN `option_non_zero_usize_incorrect` redeclared with a different signature
358             fn option_non_null_ptr_incorrect() -> *const isize;
359             //~^ WARN `option_non_null_ptr_incorrect` redeclared with a different signature
360         }
361     }
362 }
363
364 #[allow(improper_ctypes)]
365 mod unknown_layout {
366     mod a {
367         extern "C" {
368             pub fn generic(l: Link<u32>);
369         }
370         pub struct Link<T> {
371             pub item: T,
372             pub next: *const Link<T>,
373         }
374     }
375
376     mod b {
377         extern "C" {
378             pub fn generic(l: Link<u32>);
379         }
380         pub struct Link<T> {
381             pub item: T,
382             pub next: *const Link<T>,
383         }
384     }
385 }
386
387 mod hidden_niche {
388     mod a {
389         extern "C" {
390             fn hidden_niche_transparent() -> usize;
391             fn hidden_niche_transparent_no_niche() -> usize;
392             fn hidden_niche_unsafe_cell() -> usize;
393         }
394     }
395     mod b {
396         use std::cell::UnsafeCell;
397         use std::num::NonZeroUsize;
398
399         #[repr(transparent)]
400         struct Transparent { x: NonZeroUsize }
401
402         #[repr(transparent)]
403         struct TransparentNoNiche { y: UnsafeCell<NonZeroUsize> }
404
405         extern "C" {
406             fn hidden_niche_transparent() -> Option<Transparent>;
407
408             fn hidden_niche_transparent_no_niche() -> Option<TransparentNoNiche>;
409             //~^ WARN redeclared with a different signature
410             //~| WARN block uses type `Option<TransparentNoNiche>`, which is not FFI-safe
411
412             fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZeroUsize>>;
413             //~^ WARN redeclared with a different signature
414             //~| WARN block uses type `Option<UnsafeCell<NonZeroUsize>>`, which is not FFI-safe
415         }
416     }
417 }