Skip to content

Commit 04f6cc5

Browse files
committed
ImproperCTypes: change handling of indirections
- Uniformise how indirections (references, Boxes, raw pointers) are handled. (more specific indirection types with specific guarantees are not handled yet) - Indirections that are compiled to a "thick pointer" (indirections to slices, dyn objects, *not* foreign !Sized types) have better messaging around them. - Now, the pointee of a FFI-safe indirection is always considered safe. This might be a regression, if we consider that an extern function's API should describe how the function can be used by the non-defining side of the FFI boundary. However, enforcing this everywhere would force the user to perform an unreasonable amount of typecasts to/from opaque pointers. There is something better to do here, but it will be left to another PR.
1 parent a319188 commit 04f6cc5

File tree

10 files changed

+336
-203
lines changed

10 files changed

+336
-203
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,6 @@ lint_improper_ctypes = {$desc} uses type `{$ty}`, which is not FFI-safe
343343
lint_improper_ctypes_array_help = consider passing a pointer to the array
344344
345345
lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe
346-
lint_improper_ctypes_box = box cannot be represented as a single pointer
347346
348347
lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead
349348

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 212 additions & 64 deletions
Large diffs are not rendered by default.

tests/ui/lint/extern-C-fnptr-lints-slices.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// It's an improper ctype (a slice) arg in an extern "C" fnptr.
44

55
pub type F = extern "C" fn(&[u8]);
6-
//~^ ERROR: `extern` callback uses type `[u8]`, which is not FFI-safe
6+
//~^ ERROR: `extern` callback uses type `&[u8]`, which is not FFI-safe
77

88

99
fn main() {}

tests/ui/lint/extern-C-fnptr-lints-slices.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
error: `extern` callback uses type `[u8]`, which is not FFI-safe
1+
error: `extern` callback uses type `&[u8]`, which is not FFI-safe
22
--> $DIR/extern-C-fnptr-lints-slices.rs:5:28
33
|
44
LL | pub type F = extern "C" fn(&[u8]);
55
| ^^^^^ not FFI-safe
66
|
77
= help: consider using a raw pointer instead
8-
= note: slices have no C equivalent
8+
= note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
99
note: the lint level is defined here
1010
--> $DIR/extern-C-fnptr-lints-slices.rs:1:8
1111
|

tests/ui/lint/improper-ctypes/lint-73249-2.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//@ check-pass // possible FIXME: see below
2+
13
#![feature(type_alias_impl_trait)]
24
#![deny(improper_ctypes)]
35

@@ -24,7 +26,8 @@ struct A<T: Foo> {
2426
}
2527

2628
extern "C" {
27-
fn lint_me() -> A<()>; //~ ERROR: uses type `Qux`
29+
// possible FIXME(ctypes): the unsafety of Qux is unseen, as it is behing a FFI-safe indirection
30+
fn lint_me() -> A<()>;
2831
}
2932

3033
fn main() {}

tests/ui/lint/improper-ctypes/lint-73249-2.stderr

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/ui/lint/improper-ctypes/lint-ctypes.rs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
#![feature(rustc_private)]
2+
#![feature(extern_types)]
23

34
#![allow(private_interfaces)]
45
#![deny(improper_ctypes)]
56

67
use std::cell::UnsafeCell;
78
use std::marker::PhantomData;
89
use std::ffi::{c_int, c_uint};
10+
use std::fmt::Debug;
911

12+
unsafe extern "C" {type UnsizedOpaque;}
1013
trait Bar { }
1114
trait Mirror { type It: ?Sized; }
1215
impl<T: ?Sized> Mirror for T { type It = Self; }
@@ -20,15 +23,15 @@ pub type I32Pair = (i32, i32);
2023
#[repr(C)]
2124
pub struct ZeroSize;
2225
pub type RustFn = fn();
23-
pub type RustBadRet = extern "C" fn() -> Box<u32>;
26+
pub type RustBoxRet = extern "C" fn() -> Box<u32>;
2427
pub type CVoidRet = ();
2528
pub struct Foo;
2629
#[repr(transparent)]
2730
pub struct TransparentI128(i128);
2831
#[repr(transparent)]
2932
pub struct TransparentStr(&'static str);
3033
#[repr(transparent)]
31-
pub struct TransparentBadFn(RustBadRet);
34+
pub struct TransparentBoxFn(RustBoxRet);
3235
#[repr(transparent)]
3336
pub struct TransparentInt(u32);
3437
#[repr(transparent)]
@@ -39,21 +42,37 @@ pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>);
3942
pub struct TransparentUnit<U>(f32, PhantomData<U>);
4043
#[repr(transparent)]
4144
pub struct TransparentCustomZst(i32, ZeroSize);
45+
#[repr(C)]
46+
pub struct UnsizedStructBecauseForeign {
47+
sized: u32,
48+
unszd: UnsizedOpaque,
49+
}
50+
#[repr(C)]
51+
pub struct UnsizedStructBecauseDyn {
52+
sized: u32,
53+
unszd: dyn Debug,
54+
}
55+
56+
#[repr(C)]
57+
pub struct TwoBadTypes<'a> {
58+
non_c_type: char,
59+
ref_with_mdata: &'a [u8],
60+
}
4261

4362
#[repr(C)]
4463
pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
4564

4665
extern "C" {
47-
pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo`
48-
pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo`
66+
pub fn ptr_type1(size: *const Foo);
67+
pub fn ptr_type2(size: *const Foo);
4968
pub fn ptr_unit(p: *const ());
50-
pub fn ptr_tuple(p: *const ((),)); //~ ERROR: uses type `((),)`
51-
pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]`
52-
pub fn str_type(p: &str); //~ ERROR: uses type `str`
53-
pub fn box_type(p: Box<u32>); //~ ERROR uses type `Box<u32>`
69+
pub fn ptr_tuple(p: *const ((),));
70+
pub fn slice_type(p: &[u32]); //~ ERROR: uses type `&[u32]`
71+
pub fn str_type(p: &str); //~ ERROR: uses type `&str`
72+
pub fn box_type(p: Box<u32>);
5473
pub fn opt_box_type(p: Option<Box<u32>>);
5574
pub fn char_type(p: char); //~ ERROR uses type `char`
56-
pub fn trait_type(p: &dyn Bar); //~ ERROR uses type `dyn Bar`
75+
pub fn trait_type(p: &dyn Bar); //~ ERROR uses type `&dyn Bar`
5776
pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)`
5877
pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)`
5978
pub fn zero_size(p: ZeroSize); //~ ERROR uses type `ZeroSize`
@@ -63,11 +82,14 @@ extern "C" {
6382
-> ::std::marker::PhantomData<bool>; //~ ERROR uses type `PhantomData<bool>`
6483
pub fn fn_type(p: RustFn); //~ ERROR uses type `fn()`
6584
pub fn fn_type2(p: fn()); //~ ERROR uses type `fn()`
66-
pub fn fn_contained(p: RustBadRet);
67-
pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str`
68-
pub fn transparent_fn(p: TransparentBadFn);
85+
pub fn fn_contained(p: RustBoxRet);
86+
pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `&str`
87+
pub fn transparent_fn(p: TransparentBoxFn);
6988
pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]`
7089

90+
pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign);
91+
pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn`
92+
7193
pub fn no_niche_a(a: Option<UnsafeCell<extern "C" fn()>>);
7294
//~^ ERROR: uses type `Option<UnsafeCell<extern "C" fn()>>`
7395
pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
Lines changed: 38 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,45 @@
1-
error: `extern` block uses type `Foo`, which is not FFI-safe
2-
--> $DIR/lint-ctypes.rs:47:28
1+
error: `extern` block uses type `&[u32]`, which is not FFI-safe
2+
--> $DIR/lint-ctypes.rs:70:26
33
|
4-
LL | pub fn ptr_type1(size: *const Foo);
5-
| ^^^^^^^^^^ not FFI-safe
6-
|
7-
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
8-
= note: this struct has unspecified layout
9-
note: the type is defined here
10-
--> $DIR/lint-ctypes.rs:25:1
4+
LL | pub fn slice_type(p: &[u32]);
5+
| ^^^^^^ not FFI-safe
116
|
12-
LL | pub struct Foo;
13-
| ^^^^^^^^^^^^^^
7+
= help: consider using a raw pointer instead
8+
= note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
149
note: the lint level is defined here
15-
--> $DIR/lint-ctypes.rs:4:9
10+
--> $DIR/lint-ctypes.rs:5:9
1611
|
1712
LL | #![deny(improper_ctypes)]
1813
| ^^^^^^^^^^^^^^^
1914

20-
error: `extern` block uses type `Foo`, which is not FFI-safe
21-
--> $DIR/lint-ctypes.rs:48:28
22-
|
23-
LL | pub fn ptr_type2(size: *const Foo);
24-
| ^^^^^^^^^^ not FFI-safe
25-
|
26-
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
27-
= note: this struct has unspecified layout
28-
note: the type is defined here
29-
--> $DIR/lint-ctypes.rs:25:1
30-
|
31-
LL | pub struct Foo;
32-
| ^^^^^^^^^^^^^^
33-
34-
error: `extern` block uses type `((),)`, which is not FFI-safe
35-
--> $DIR/lint-ctypes.rs:50:25
36-
|
37-
LL | pub fn ptr_tuple(p: *const ((),));
38-
| ^^^^^^^^^^^^ not FFI-safe
39-
|
40-
= help: consider using a struct instead
41-
= note: tuples have unspecified layout
42-
43-
error: `extern` block uses type `[u32]`, which is not FFI-safe
44-
--> $DIR/lint-ctypes.rs:51:26
45-
|
46-
LL | pub fn slice_type(p: &[u32]);
47-
| ^^^^^^ not FFI-safe
48-
|
49-
= help: consider using a raw pointer instead
50-
= note: slices have no C equivalent
51-
52-
error: `extern` block uses type `str`, which is not FFI-safe
53-
--> $DIR/lint-ctypes.rs:52:24
15+
error: `extern` block uses type `&str`, which is not FFI-safe
16+
--> $DIR/lint-ctypes.rs:71:24
5417
|
5518
LL | pub fn str_type(p: &str);
5619
| ^^^^ not FFI-safe
5720
|
5821
= help: consider using `*const u8` and a length instead
59-
= note: string slices have no C equivalent
60-
61-
error: `extern` block uses type `Box<u32>`, which is not FFI-safe
62-
--> $DIR/lint-ctypes.rs:53:24
63-
|
64-
LL | pub fn box_type(p: Box<u32>);
65-
| ^^^^^^^^ not FFI-safe
66-
|
67-
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
68-
= note: this struct has unspecified layout
22+
= note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
6923

7024
error: `extern` block uses type `char`, which is not FFI-safe
71-
--> $DIR/lint-ctypes.rs:55:25
25+
--> $DIR/lint-ctypes.rs:74:25
7226
|
7327
LL | pub fn char_type(p: char);
7428
| ^^^^ not FFI-safe
7529
|
7630
= help: consider using `u32` or `libc::wchar_t` instead
7731
= note: the `char` type has no C equivalent
7832

79-
error: `extern` block uses type `dyn Bar`, which is not FFI-safe
80-
--> $DIR/lint-ctypes.rs:56:26
33+
error: `extern` block uses type `&dyn Bar`, which is not FFI-safe
34+
--> $DIR/lint-ctypes.rs:75:26
8135
|
8236
LL | pub fn trait_type(p: &dyn Bar);
8337
| ^^^^^^^^ not FFI-safe
8438
|
85-
= note: trait objects have no C equivalent
39+
= note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
8640

8741
error: `extern` block uses type `(i32, i32)`, which is not FFI-safe
88-
--> $DIR/lint-ctypes.rs:57:26
42+
--> $DIR/lint-ctypes.rs:76:26
8943
|
9044
LL | pub fn tuple_type(p: (i32, i32));
9145
| ^^^^^^^^^^ not FFI-safe
@@ -94,7 +48,7 @@ LL | pub fn tuple_type(p: (i32, i32));
9448
= note: tuples have unspecified layout
9549

9650
error: `extern` block uses type `(i32, i32)`, which is not FFI-safe
97-
--> $DIR/lint-ctypes.rs:58:27
51+
--> $DIR/lint-ctypes.rs:77:27
9852
|
9953
LL | pub fn tuple_type2(p: I32Pair);
10054
| ^^^^^^^ not FFI-safe
@@ -103,42 +57,42 @@ LL | pub fn tuple_type2(p: I32Pair);
10357
= note: tuples have unspecified layout
10458

10559
error: `extern` block uses type `ZeroSize`, which is not FFI-safe
106-
--> $DIR/lint-ctypes.rs:59:25
60+
--> $DIR/lint-ctypes.rs:78:25
10761
|
10862
LL | pub fn zero_size(p: ZeroSize);
10963
| ^^^^^^^^ not FFI-safe
11064
|
11165
= help: consider adding a member to this struct
11266
= note: this struct has no fields
11367
note: the type is defined here
114-
--> $DIR/lint-ctypes.rs:21:1
68+
--> $DIR/lint-ctypes.rs:24:1
11569
|
11670
LL | pub struct ZeroSize;
11771
| ^^^^^^^^^^^^^^^^^^^
11872

11973
error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe
120-
--> $DIR/lint-ctypes.rs:60:33
74+
--> $DIR/lint-ctypes.rs:79:33
12175
|
12276
LL | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData);
12377
| ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
12478
|
12579
= note: composed only of `PhantomData`
12680
note: the type is defined here
127-
--> $DIR/lint-ctypes.rs:44:1
81+
--> $DIR/lint-ctypes.rs:63:1
12882
|
12983
LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
13084
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13185

13286
error: `extern` block uses type `PhantomData<bool>`, which is not FFI-safe
133-
--> $DIR/lint-ctypes.rs:63:12
87+
--> $DIR/lint-ctypes.rs:82:12
13488
|
13589
LL | -> ::std::marker::PhantomData<bool>;
13690
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
13791
|
13892
= note: composed only of `PhantomData`
13993

14094
error: `extern` block uses type `fn()`, which is not FFI-safe
141-
--> $DIR/lint-ctypes.rs:64:23
95+
--> $DIR/lint-ctypes.rs:83:23
14296
|
14397
LL | pub fn fn_type(p: RustFn);
14498
| ^^^^^^ not FFI-safe
@@ -147,34 +101,42 @@ LL | pub fn fn_type(p: RustFn);
147101
= note: this function pointer has Rust-specific calling convention
148102

149103
error: `extern` block uses type `fn()`, which is not FFI-safe
150-
--> $DIR/lint-ctypes.rs:65:24
104+
--> $DIR/lint-ctypes.rs:84:24
151105
|
152106
LL | pub fn fn_type2(p: fn());
153107
| ^^^^ not FFI-safe
154108
|
155109
= help: consider using an `extern fn(...) -> ...` function pointer instead
156110
= note: this function pointer has Rust-specific calling convention
157111

158-
error: `extern` block uses type `str`, which is not FFI-safe
159-
--> $DIR/lint-ctypes.rs:67:31
112+
error: `extern` block uses type `&str`, which is not FFI-safe
113+
--> $DIR/lint-ctypes.rs:86:31
160114
|
161115
LL | pub fn transparent_str(p: TransparentStr);
162116
| ^^^^^^^^^^^^^^ not FFI-safe
163117
|
164118
= help: consider using `*const u8` and a length instead
165-
= note: string slices have no C equivalent
119+
= note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
166120

167121
error: `extern` block uses type `[u8; 8]`, which is not FFI-safe
168-
--> $DIR/lint-ctypes.rs:69:27
122+
--> $DIR/lint-ctypes.rs:88:27
169123
|
170124
LL | pub fn raw_array(arr: [u8; 8]);
171125
| ^^^^^^^ not FFI-safe
172126
|
173127
= help: consider passing a pointer to the array
174128
= note: passing raw arrays by value is not FFI-safe
175129

130+
error: `extern` block uses type `&UnsizedStructBecauseDyn`, which is not FFI-safe
131+
--> $DIR/lint-ctypes.rs:91:47
132+
|
133+
LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn);
134+
| ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
135+
|
136+
= note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
137+
176138
error: `extern` block uses type `Option<UnsafeCell<extern "C" fn()>>`, which is not FFI-safe
177-
--> $DIR/lint-ctypes.rs:71:26
139+
--> $DIR/lint-ctypes.rs:93:26
178140
|
179141
LL | pub fn no_niche_a(a: Option<UnsafeCell<extern "C" fn()>>);
180142
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -183,13 +145,13 @@ LL | pub fn no_niche_a(a: Option<UnsafeCell<extern "C" fn()>>);
183145
= note: enum has no representation hint
184146

185147
error: `extern` block uses type `Option<UnsafeCell<&i32>>`, which is not FFI-safe
186-
--> $DIR/lint-ctypes.rs:73:26
148+
--> $DIR/lint-ctypes.rs:95:26
187149
|
188150
LL | pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
189151
| ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
190152
|
191153
= help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
192154
= note: enum has no representation hint
193155

194-
error: aborting due to 19 previous errors
156+
error: aborting due to 16 previous errors
195157

0 commit comments

Comments
 (0)