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