]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | // check-pass |
2 | // aux-build:external_extern_fn.rs | |
3 | #![crate_type = "lib"] | |
4 | #![warn(clashing_extern_declarations)] | |
5 | ||
3dfed10e XL |
6 | mod redeclared_different_signature { |
7 | mod a { | |
8 | extern "C" { | |
9 | fn clash(x: u8); | |
10 | } | |
f035d41b | 11 | } |
3dfed10e XL |
12 | mod b { |
13 | extern "C" { | |
14 | fn clash(x: u64); //~ WARN `clash` redeclared with a different signature | |
15 | } | |
f035d41b XL |
16 | } |
17 | } | |
18 | ||
3dfed10e XL |
19 | mod redeclared_same_signature { |
20 | mod a { | |
21 | extern "C" { | |
22 | fn no_clash(x: u8); | |
23 | } | |
f035d41b | 24 | } |
3dfed10e XL |
25 | mod b { |
26 | extern "C" { | |
27 | fn no_clash(x: u8); | |
28 | } | |
f035d41b XL |
29 | } |
30 | } | |
31 | ||
3dfed10e XL |
32 | extern crate external_extern_fn; |
33 | mod extern_no_clash { | |
34 | // Should not clash with external_extern_fn::extern_fn. | |
f035d41b | 35 | extern "C" { |
3dfed10e | 36 | fn extern_fn(x: u8); |
f035d41b XL |
37 | } |
38 | } | |
39 | ||
f035d41b XL |
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 | |
f035d41b XL |
125 | // This should not trigger the lint because two::Banana is structurally equivalent to |
126 | // one::Banana. | |
3dfed10e | 127 | extern "C" { |
f035d41b XL |
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" { | |
3dfed10e XL |
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 | } | |
1b1a35ee XL |
185 | extern "C" { |
186 | fn origin() -> Point3; | |
187 | } | |
3dfed10e XL |
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 | } | |
1b1a35ee XL |
196 | extern "C" { |
197 | fn origin() -> Point3; //~ WARN `origin` redeclared with a different signature | |
198 | } | |
3dfed10e XL |
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 | ||
1b1a35ee XL |
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 | ||
3dfed10e XL |
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 | |
f035d41b XL |
360 | } |
361 | } | |
362 | } | |
1b1a35ee XL |
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 | } |