Skip to content

Commit 7962a2d

Browse files
committed
lint: fix ImproperCTypes edge case for unsized structs due to foreign types
1 parent 1d52131 commit 7962a2d

File tree

3 files changed

+119
-32
lines changed

3 files changed

+119
-32
lines changed

compiler/rustc_lint/src/types.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,69 @@ enum FfiResult<'tcx> {
740740
},
741741
}
742742

743+
pub(crate) fn is_unsized_because_foreign<'tcx, 'a>(
744+
cx: &'a LateContext<'tcx>,
745+
ty: Ty<'tcx>,
746+
) -> Result<bool, ()> {
747+
let tcx = cx.tcx;
748+
749+
if ty.is_sized(tcx, cx.typing_env()) {
750+
Err(())
751+
} else {
752+
Ok(match ty.kind() {
753+
ty::Slice(_) => false,
754+
ty::Str => false,
755+
ty::Dynamic(..) => false,
756+
ty::Foreign(..) => true,
757+
ty::Alias(ty::Opaque, ..) => todo!("why"),
758+
ty::Adt(def, args) => {
759+
// for now assume: boxes and phantoms don't mess with this
760+
match def.adt_kind() {
761+
AdtKind::Union | AdtKind::Enum => true,
762+
AdtKind::Struct => {
763+
if let Some(sym::cstring_type | sym::cstr_type) =
764+
tcx.get_diagnostic_name(def.did())
765+
{
766+
return Ok(false);
767+
}
768+
// FIXME: how do we deal with non-exhaustive unsized structs/unions?
769+
770+
if def.non_enum_variant().fields.is_empty() {
771+
bug!("empty unsized struct/union. what?");
772+
}
773+
774+
let variant = def.non_enum_variant();
775+
776+
// only the last field may be unsized
777+
let n_fields = variant.fields.len();
778+
let last_field = &variant.fields[(n_fields - 1).into()];
779+
let field_ty = last_field.ty(cx.tcx, args);
780+
let field_ty = cx
781+
.tcx
782+
.try_normalize_erasing_regions(cx.typing_env(), field_ty)
783+
.unwrap_or(field_ty);
784+
return Ok(is_unsized_because_foreign(cx, field_ty).unwrap());
785+
}
786+
}
787+
}
788+
ty::Tuple(tuple) => {
789+
// only the last field may be unsized
790+
let n_fields = tuple.len();
791+
let field_ty: Ty<'tcx> = tuple[n_fields - 1];
792+
//let field_ty = last_field.ty(cx.tcx, args);
793+
let field_ty = cx
794+
.tcx
795+
.try_normalize_erasing_regions(cx.typing_env(), field_ty)
796+
.unwrap_or(field_ty);
797+
is_unsized_because_foreign(cx, field_ty).unwrap()
798+
}
799+
t => {
800+
bug!("we shouldn't be looking if this is unsized for a reason or another: {:?}", t)
801+
}
802+
})
803+
}
804+
}
805+
743806
pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
744807
tcx: TyCtxt<'tcx>,
745808
def: ty::AdtDef<'tcx>,
@@ -1044,7 +1107,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10441107
ty::Adt(def, args) => {
10451108
if let Some(inner_ty) = ty.boxed_ty() {
10461109
if inner_ty.is_sized(tcx, self.cx.typing_env())
1047-
|| matches!(inner_ty.kind(), ty::Foreign(..))
1110+
|| is_unsized_because_foreign(self.cx, inner_ty).unwrap()
10481111
{
10491112
// discussion on declaration vs definition:
10501113
// see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
@@ -1249,7 +1312,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12491312

12501313
ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => {
12511314
if inner_ty.is_sized(tcx, self.cx.typing_env())
1252-
|| matches!(inner_ty.kind(), ty::Foreign(..))
1315+
|| is_unsized_because_foreign(self.cx, inner_ty).unwrap()
12531316
{
12541317
// there's a nuance on what this lint should do for function definitions
12551318
// (touched upon in https://github.com/rust-lang/rust/issues/66220 and https://github.com/rust-lang/rust/pull/72700)

tests/ui/lint/lint-ctypes.rs

Lines changed: 16 additions & 0 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; }
@@ -39,6 +42,16 @@ 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+
}
4255

4356
#[repr(C)]
4457
pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
@@ -72,6 +85,9 @@ extern "C" {
7285
pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `Box<u32>`
7386
pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]`
7487

88+
pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign);
89+
pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn`
90+
7591
pub fn no_niche_a(a: Option<UnsafeCell<extern fn()>>);
7692
//~^ ERROR: uses type `Option<UnsafeCell<extern "C" fn()>>`
7793
pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);

tests/ui/lint/lint-ctypes.stderr

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: `extern` block uses type `Foo`, which is not FFI-safe
2-
--> $DIR/lint-ctypes.rs:47:28
2+
--> $DIR/lint-ctypes.rs:60:28
33
|
44
LL | pub fn ptr_type1(size: *const Foo);
55
| ^^^^^^^^^^ not FFI-safe
@@ -8,18 +8,18 @@ LL | pub fn ptr_type1(size: *const Foo);
88
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
99
= note: this struct has unspecified layout
1010
note: the type is defined here
11-
--> $DIR/lint-ctypes.rs:25:1
11+
--> $DIR/lint-ctypes.rs:28:1
1212
|
1313
LL | pub struct Foo;
1414
| ^^^^^^^^^^^^^^
1515
note: the lint level is defined here
16-
--> $DIR/lint-ctypes.rs:4:9
16+
--> $DIR/lint-ctypes.rs:5:9
1717
|
1818
LL | #![deny(improper_ctypes)]
1919
| ^^^^^^^^^^^^^^^
2020

2121
error: `extern` block uses type `Foo`, which is not FFI-safe
22-
--> $DIR/lint-ctypes.rs:48:28
22+
--> $DIR/lint-ctypes.rs:61:28
2323
|
2424
LL | pub fn ptr_type2(size: *const Foo);
2525
| ^^^^^^^^^^ not FFI-safe
@@ -28,13 +28,13 @@ LL | pub fn ptr_type2(size: *const Foo);
2828
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
2929
= note: this struct has unspecified layout
3030
note: the type is defined here
31-
--> $DIR/lint-ctypes.rs:25:1
31+
--> $DIR/lint-ctypes.rs:28:1
3232
|
3333
LL | pub struct Foo;
3434
| ^^^^^^^^^^^^^^
3535

3636
error: `extern` block uses type `((),)`, which is not FFI-safe
37-
--> $DIR/lint-ctypes.rs:50:25
37+
--> $DIR/lint-ctypes.rs:63:25
3838
|
3939
LL | pub fn ptr_tuple(p: *const ((),));
4040
| ^^^^^^^^^^^^ not FFI-safe
@@ -44,7 +44,7 @@ LL | pub fn ptr_tuple(p: *const ((),));
4444
= note: tuples have unspecified layout
4545

4646
error: `extern` block uses type `&[u32]`, which is not FFI-safe
47-
--> $DIR/lint-ctypes.rs:51:26
47+
--> $DIR/lint-ctypes.rs:64:26
4848
|
4949
LL | pub fn slice_type(p: &[u32]);
5050
| ^^^^^^ not FFI-safe
@@ -53,7 +53,7 @@ LL | pub fn slice_type(p: &[u32]);
5353
= note: this reference to an unsized rust type contains metadata, which makes it incompatible with a C pointer
5454

5555
error: `extern` block uses type `&str`, which is not FFI-safe
56-
--> $DIR/lint-ctypes.rs:52:24
56+
--> $DIR/lint-ctypes.rs:65:24
5757
|
5858
LL | pub fn str_type(p: &str);
5959
| ^^^^ not FFI-safe
@@ -71,7 +71,7 @@ LL | pub fn box_type(p: Box<u32>);
7171
= note: this struct has unspecified layout
7272

7373
error: `extern` block uses type `Option<Box<u32>>`, which is not FFI-safe
74-
--> $DIR/lint-ctypes.rs:54:28
74+
--> $DIR/lint-ctypes.rs:67:28
7575
|
7676
LL | pub fn opt_box_type(p: Option<Box<u32>>);
7777
| ^^^^^^^^^^^^^^^^ not FFI-safe
@@ -80,7 +80,7 @@ LL | pub fn opt_box_type(p: Option<Box<u32>>);
8080
= note: enum has no representation hint
8181

8282
error: `extern` block uses type `char`, which is not FFI-safe
83-
--> $DIR/lint-ctypes.rs:56:25
83+
--> $DIR/lint-ctypes.rs:69:25
8484
|
8585
LL | pub fn char_type(p: char);
8686
| ^^^^ not FFI-safe
@@ -89,31 +89,31 @@ LL | pub fn char_type(p: char);
8989
= note: the `char` type has no C equivalent
9090

9191
error: `extern` block uses type `i128`, which is not FFI-safe
92-
--> $DIR/lint-ctypes.rs:57:25
92+
--> $DIR/lint-ctypes.rs:70:25
9393
|
9494
LL | pub fn i128_type(p: i128);
9595
| ^^^^ not FFI-safe
9696
|
9797
= note: 128-bit integers don't currently have a known stable ABI
9898

9999
error: `extern` block uses type `u128`, which is not FFI-safe
100-
--> $DIR/lint-ctypes.rs:58:25
100+
--> $DIR/lint-ctypes.rs:71:25
101101
|
102102
LL | pub fn u128_type(p: u128);
103103
| ^^^^ not FFI-safe
104104
|
105105
= note: 128-bit integers don't currently have a known stable ABI
106106

107107
error: `extern` block uses type `&dyn Bar`, which is not FFI-safe
108-
--> $DIR/lint-ctypes.rs:59:26
108+
--> $DIR/lint-ctypes.rs:72:26
109109
|
110110
LL | pub fn trait_type(p: &dyn Bar);
111111
| ^^^^^^^^ not FFI-safe
112112
|
113113
= note: this reference to an unsized rust type contains metadata, which makes it incompatible with a C pointer
114114

115115
error: `extern` block uses type `(i32, i32)`, which is not FFI-safe
116-
--> $DIR/lint-ctypes.rs:60:26
116+
--> $DIR/lint-ctypes.rs:73:26
117117
|
118118
LL | pub fn tuple_type(p: (i32, i32));
119119
| ^^^^^^^^^^ not FFI-safe
@@ -122,7 +122,7 @@ LL | pub fn tuple_type(p: (i32, i32));
122122
= note: tuples have unspecified layout
123123

124124
error: `extern` block uses type `(i32, i32)`, which is not FFI-safe
125-
--> $DIR/lint-ctypes.rs:61:27
125+
--> $DIR/lint-ctypes.rs:74:27
126126
|
127127
LL | pub fn tuple_type2(p: I32Pair);
128128
| ^^^^^^^ not FFI-safe
@@ -131,42 +131,42 @@ LL | pub fn tuple_type2(p: I32Pair);
131131
= note: tuples have unspecified layout
132132

133133
error: `extern` block uses type `ZeroSize`, which is not FFI-safe
134-
--> $DIR/lint-ctypes.rs:62:25
134+
--> $DIR/lint-ctypes.rs:75:25
135135
|
136136
LL | pub fn zero_size(p: ZeroSize);
137137
| ^^^^^^^^ not FFI-safe
138138
|
139139
= help: consider adding a member to this struct
140140
= note: this struct has no fields
141141
note: the type is defined here
142-
--> $DIR/lint-ctypes.rs:21:1
142+
--> $DIR/lint-ctypes.rs:24:1
143143
|
144144
LL | pub struct ZeroSize;
145145
| ^^^^^^^^^^^^^^^^^^^
146146

147147
error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe
148-
--> $DIR/lint-ctypes.rs:63:33
148+
--> $DIR/lint-ctypes.rs:76:33
149149
|
150150
LL | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData);
151151
| ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
152152
|
153153
= note: composed only of `PhantomData`
154154
note: the type is defined here
155-
--> $DIR/lint-ctypes.rs:44:1
155+
--> $DIR/lint-ctypes.rs:57:1
156156
|
157157
LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
158158
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
159159

160160
error: `extern` block uses type `PhantomData<bool>`, which is not FFI-safe
161-
--> $DIR/lint-ctypes.rs:66:12
161+
--> $DIR/lint-ctypes.rs:79:12
162162
|
163163
LL | -> ::std::marker::PhantomData<bool>;
164164
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
165165
|
166166
= note: composed only of `PhantomData`
167167

168168
error: `extern` block uses type `fn()`, which is not FFI-safe
169-
--> $DIR/lint-ctypes.rs:67:23
169+
--> $DIR/lint-ctypes.rs:80:23
170170
|
171171
LL | pub fn fn_type(p: RustFn);
172172
| ^^^^^^ not FFI-safe
@@ -175,7 +175,7 @@ LL | pub fn fn_type(p: RustFn);
175175
= note: this function pointer has Rust-specific calling convention
176176

177177
error: `extern` block uses type `fn()`, which is not FFI-safe
178-
--> $DIR/lint-ctypes.rs:68:24
178+
--> $DIR/lint-ctypes.rs:81:24
179179
|
180180
LL | pub fn fn_type2(p: fn());
181181
| ^^^^ not FFI-safe
@@ -193,15 +193,15 @@ LL | pub fn fn_contained(p: RustBadRet);
193193
= note: this struct has unspecified layout
194194

195195
error: `extern` block uses type `i128`, which is not FFI-safe
196-
--> $DIR/lint-ctypes.rs:70:32
196+
--> $DIR/lint-ctypes.rs:83:32
197197
|
198198
LL | pub fn transparent_i128(p: TransparentI128);
199199
| ^^^^^^^^^^^^^^^ not FFI-safe
200200
|
201201
= note: 128-bit integers don't currently have a known stable ABI
202202

203203
error: `extern` block uses type `&str`, which is not FFI-safe
204-
--> $DIR/lint-ctypes.rs:71:31
204+
--> $DIR/lint-ctypes.rs:84:31
205205
|
206206
LL | pub fn transparent_str(p: TransparentStr);
207207
| ^^^^^^^^^^^^^^ not FFI-safe
@@ -219,16 +219,24 @@ LL | pub fn transparent_fn(p: TransparentBadFn);
219219
= note: this struct has unspecified layout
220220

221221
error: `extern` block uses type `[u8; 8]`, which is not FFI-safe
222-
--> $DIR/lint-ctypes.rs:73:27
222+
--> $DIR/lint-ctypes.rs:86:27
223223
|
224224
LL | pub fn raw_array(arr: [u8; 8]);
225225
| ^^^^^^^ not FFI-safe
226226
|
227227
= help: consider passing a pointer to the array
228228
= note: passing raw arrays by value is not FFI-safe
229229

230+
error: `extern` block uses type `&UnsizedStructBecauseDyn`, which is not FFI-safe
231+
--> $DIR/lint-ctypes.rs:89:47
232+
|
233+
LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn);
234+
| ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
235+
|
236+
= note: this reference to an unsized rust type contains metadata, which makes it incompatible with a C pointer
237+
230238
error: `extern` block uses type `Option<UnsafeCell<extern "C" fn()>>`, which is not FFI-safe
231-
--> $DIR/lint-ctypes.rs:75:26
239+
--> $DIR/lint-ctypes.rs:91:26
232240
|
233241
LL | pub fn no_niche_a(a: Option<UnsafeCell<extern fn()>>);
234242
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -237,7 +245,7 @@ LL | pub fn no_niche_a(a: Option<UnsafeCell<extern fn()>>);
237245
= note: enum has no representation hint
238246

239247
error: `extern` block uses type `Option<UnsafeCell<&i32>>`, which is not FFI-safe
240-
--> $DIR/lint-ctypes.rs:77:26
248+
--> $DIR/lint-ctypes.rs:93:26
241249
|
242250
LL | pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
243251
| ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -246,19 +254,19 @@ LL | pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
246254
= note: enum has no representation hint
247255

248256
error: `extern` block uses type `u128`, which is not FFI-safe
249-
--> $DIR/lint-ctypes.rs:80:34
257+
--> $DIR/lint-ctypes.rs:96:34
250258
|
251259
LL | pub static static_u128_type: u128;
252260
| ^^^^ not FFI-safe
253261
|
254262
= note: 128-bit integers don't currently have a known stable ABI
255263

256264
error: `extern` block uses type `u128`, which is not FFI-safe
257-
--> $DIR/lint-ctypes.rs:81:40
265+
--> $DIR/lint-ctypes.rs:97:40
258266
|
259267
LL | pub static static_u128_array_type: [u128; 16];
260268
| ^^^^^^^^^^ not FFI-safe
261269
|
262270
= note: 128-bit integers don't currently have a known stable ABI
263271

264-
error: aborting due to 27 previous errors
272+
error: aborting due to 29 previous errors

0 commit comments

Comments
 (0)