Skip to content

Commit 2d6ee1f

Browse files
authored
Rollup merge of rust-lang#128735 - jieyouxu:pr-120176-revive, r=cjgillot
Add a special case for `CStr`/`CString` in the `improper_ctypes` lint Revives rust-lang#120176. Just needed to bless a test and fix an argument, but seemed reasonable to me otherwise. Instead of saying to "consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct", we now tell users to "Use `*const ffi::c_char` instead, and pass the value from `CStr::as_ptr()`" when the type involved is a `CStr` or a `CString`. The suggestion is not made for `&mut CString` or `*mut CString`. r? ````@cjgillot```` (since you were the reviewer of the original PR rust-lang#120176, but feel free to reroll)
2 parents 05c3360 + b335ec9 commit 2d6ee1f

File tree

7 files changed

+172
-21
lines changed

7 files changed

+172
-21
lines changed

compiler/rustc_lint/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,11 @@ lint_improper_ctypes_box = box cannot be represented as a single pointer
361361
lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead
362362
363363
lint_improper_ctypes_char_reason = the `char` type has no C equivalent
364+
365+
lint_improper_ctypes_cstr_help =
366+
consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
367+
lint_improper_ctypes_cstr_reason = `CStr`/`CString` do not have a guaranteed layout
368+
364369
lint_improper_ctypes_dyn = trait objects have no C equivalent
365370
366371
lint_improper_ctypes_enum_repr_help =

compiler/rustc_lint/src/types.rs

+39-15
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,14 @@ struct ImproperCTypesVisitor<'a, 'tcx> {
985985
mode: CItemKind,
986986
}
987987

988+
/// Accumulator for recursive ffi type checking
989+
struct CTypesVisitorState<'tcx> {
990+
cache: FxHashSet<Ty<'tcx>>,
991+
/// The original type being checked, before we recursed
992+
/// to any other types it contains.
993+
base_ty: Ty<'tcx>,
994+
}
995+
988996
enum FfiResult<'tcx> {
989997
FfiSafe,
990998
FfiPhantom(Ty<'tcx>),
@@ -1213,7 +1221,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12131221
/// Checks if the given field's type is "ffi-safe".
12141222
fn check_field_type_for_ffi(
12151223
&self,
1216-
cache: &mut FxHashSet<Ty<'tcx>>,
1224+
acc: &mut CTypesVisitorState<'tcx>,
12171225
field: &ty::FieldDef,
12181226
args: GenericArgsRef<'tcx>,
12191227
) -> FfiResult<'tcx> {
@@ -1223,13 +1231,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12231231
.tcx
12241232
.try_normalize_erasing_regions(self.cx.param_env, field_ty)
12251233
.unwrap_or(field_ty);
1226-
self.check_type_for_ffi(cache, field_ty)
1234+
self.check_type_for_ffi(acc, field_ty)
12271235
}
12281236

12291237
/// Checks if the given `VariantDef`'s field types are "ffi-safe".
12301238
fn check_variant_for_ffi(
12311239
&self,
1232-
cache: &mut FxHashSet<Ty<'tcx>>,
1240+
acc: &mut CTypesVisitorState<'tcx>,
12331241
ty: Ty<'tcx>,
12341242
def: ty::AdtDef<'tcx>,
12351243
variant: &ty::VariantDef,
@@ -1239,7 +1247,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12391247
let transparent_with_all_zst_fields = if def.repr().transparent() {
12401248
if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
12411249
// Transparent newtypes have at most one non-ZST field which needs to be checked..
1242-
match self.check_field_type_for_ffi(cache, field, args) {
1250+
match self.check_field_type_for_ffi(acc, field, args) {
12431251
FfiUnsafe { ty, .. } if ty.is_unit() => (),
12441252
r => return r,
12451253
}
@@ -1257,7 +1265,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12571265
// We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
12581266
let mut all_phantom = !variant.fields.is_empty();
12591267
for field in &variant.fields {
1260-
all_phantom &= match self.check_field_type_for_ffi(cache, field, args) {
1268+
all_phantom &= match self.check_field_type_for_ffi(acc, field, args) {
12611269
FfiSafe => false,
12621270
// `()` fields are FFI-safe!
12631271
FfiUnsafe { ty, .. } if ty.is_unit() => false,
@@ -1277,7 +1285,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12771285

12781286
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
12791287
/// representation which can be exported to C code).
1280-
fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1288+
fn check_type_for_ffi(
1289+
&self,
1290+
acc: &mut CTypesVisitorState<'tcx>,
1291+
ty: Ty<'tcx>,
1292+
) -> FfiResult<'tcx> {
12811293
use FfiResult::*;
12821294

12831295
let tcx = self.cx.tcx;
@@ -1286,7 +1298,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12861298
// `struct S(*mut S);`.
12871299
// FIXME: A recursion limit is necessary as well, for irregular
12881300
// recursive types.
1289-
if !cache.insert(ty) {
1301+
if !acc.cache.insert(ty) {
12901302
return FfiSafe;
12911303
}
12921304

@@ -1308,6 +1320,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13081320
}
13091321
match def.adt_kind() {
13101322
AdtKind::Struct | AdtKind::Union => {
1323+
if let Some(sym::cstring_type | sym::cstr_type) =
1324+
tcx.get_diagnostic_name(def.did())
1325+
&& !acc.base_ty.is_mutable_ptr()
1326+
{
1327+
return FfiUnsafe {
1328+
ty,
1329+
reason: fluent::lint_improper_ctypes_cstr_reason,
1330+
help: Some(fluent::lint_improper_ctypes_cstr_help),
1331+
};
1332+
}
1333+
13111334
if !def.repr().c() && !def.repr().transparent() {
13121335
return FfiUnsafe {
13131336
ty,
@@ -1354,7 +1377,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13541377
};
13551378
}
13561379

1357-
self.check_variant_for_ffi(cache, ty, def, def.non_enum_variant(), args)
1380+
self.check_variant_for_ffi(acc, ty, def, def.non_enum_variant(), args)
13581381
}
13591382
AdtKind::Enum => {
13601383
if def.variants().is_empty() {
@@ -1378,7 +1401,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13781401
if let Some(ty) =
13791402
repr_nullable_ptr(self.cx.tcx, self.cx.param_env, ty, self.mode)
13801403
{
1381-
return self.check_type_for_ffi(cache, ty);
1404+
return self.check_type_for_ffi(acc, ty);
13821405
}
13831406

13841407
return FfiUnsafe {
@@ -1399,7 +1422,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13991422
};
14001423
}
14011424

1402-
match self.check_variant_for_ffi(cache, ty, def, variant, args) {
1425+
match self.check_variant_for_ffi(acc, ty, def, variant, args) {
14031426
FfiSafe => (),
14041427
r => return r,
14051428
}
@@ -1469,9 +1492,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14691492
FfiSafe
14701493
}
14711494

1472-
ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(cache, ty),
1495+
ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty),
14731496

1474-
ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty),
1497+
ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),
14751498

14761499
ty::FnPtr(sig_tys, hdr) => {
14771500
let sig = sig_tys.with(hdr);
@@ -1485,7 +1508,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14851508

14861509
let sig = tcx.instantiate_bound_regions_with_erased(sig);
14871510
for arg in sig.inputs() {
1488-
match self.check_type_for_ffi(cache, *arg) {
1511+
match self.check_type_for_ffi(acc, *arg) {
14891512
FfiSafe => {}
14901513
r => return r,
14911514
}
@@ -1496,7 +1519,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14961519
return FfiSafe;
14971520
}
14981521

1499-
self.check_type_for_ffi(cache, ret_ty)
1522+
self.check_type_for_ffi(acc, ret_ty)
15001523
}
15011524

15021525
ty::Foreign(..) => FfiSafe,
@@ -1619,7 +1642,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
16191642
return;
16201643
}
16211644

1622-
match self.check_type_for_ffi(&mut FxHashSet::default(), ty) {
1645+
let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty };
1646+
match self.check_type_for_ffi(&mut acc, ty) {
16231647
FfiResult::FfiSafe => {}
16241648
FfiResult::FfiPhantom(ty) => {
16251649
self.emit_ffi_unsafe_type_lint(

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ symbols! {
673673
crate_visibility_modifier,
674674
crt_dash_static: "crt-static",
675675
csky_target_feature,
676+
cstr_type,
676677
cstring_type,
677678
ctlz,
678679
ctlz_nonzero,

library/core/src/ffi/c_str.rs

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ use crate::{fmt, intrinsics, ops, slice, str};
9191
/// [str]: prim@str "str"
9292
#[derive(PartialEq, Eq, Hash)]
9393
#[stable(feature = "core_c_str", since = "1.64.0")]
94+
#[rustc_diagnostic_item = "cstr_type"]
9495
#[rustc_has_incoherent_inherent_impls]
9596
#[lang = "CStr"]
9697
// `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies

tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
warning: `extern` fn uses type `[i8 or u8 (arch dependant)]`, which is not FFI-safe
1+
warning: `extern` fn uses type `CStr`, which is not FFI-safe
22
--> $DIR/extern-C-non-FFI-safe-arg-ice-52334.rs:7:12
33
|
44
LL | type Foo = extern "C" fn(::std::ffi::CStr);
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
66
|
7-
= help: consider using a raw pointer instead
8-
= note: slices have no C equivalent
7+
= help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
8+
= note: `CStr`/`CString` do not have a guaranteed layout
99
= note: `#[warn(improper_ctypes_definitions)]` on by default
1010

11-
warning: `extern` block uses type `[i8 or u8 (arch dependant)]`, which is not FFI-safe
11+
warning: `extern` block uses type `CStr`, which is not FFI-safe
1212
--> $DIR/extern-C-non-FFI-safe-arg-ice-52334.rs:10:18
1313
|
1414
LL | fn meh(blah: Foo);
1515
| ^^^ not FFI-safe
1616
|
17-
= help: consider using a raw pointer instead
18-
= note: slices have no C equivalent
17+
= help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
18+
= note: `CStr`/`CString` do not have a guaranteed layout
1919
= note: `#[warn(improper_ctypes)]` on by default
2020

2121
warning: 2 warnings emitted

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

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![crate_type = "lib"]
2+
#![deny(improper_ctypes, improper_ctypes_definitions)]
3+
4+
use std::ffi::{CStr, CString};
5+
6+
extern "C" {
7+
fn take_cstr(s: CStr);
8+
//~^ ERROR `extern` block uses type `CStr`, which is not FFI-safe
9+
//~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
10+
fn take_cstr_ref(s: &CStr);
11+
//~^ ERROR `extern` block uses type `CStr`, which is not FFI-safe
12+
//~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
13+
fn take_cstring(s: CString);
14+
//~^ ERROR `extern` block uses type `CString`, which is not FFI-safe
15+
//~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
16+
fn take_cstring_ref(s: &CString);
17+
//~^ ERROR `extern` block uses type `CString`, which is not FFI-safe
18+
//~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
19+
20+
fn no_special_help_for_mut_cstring(s: *mut CString);
21+
//~^ ERROR `extern` block uses type `CString`, which is not FFI-safe
22+
//~| HELP consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
23+
24+
fn no_special_help_for_mut_cstring_ref(s: &mut CString);
25+
//~^ ERROR `extern` block uses type `CString`, which is not FFI-safe
26+
//~| HELP consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
27+
}
28+
29+
extern "C" fn rust_take_cstr_ref(s: &CStr) {}
30+
//~^ ERROR `extern` fn uses type `CStr`, which is not FFI-safe
31+
//~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
32+
extern "C" fn rust_take_cstring(s: CString) {}
33+
//~^ ERROR `extern` fn uses type `CString`, which is not FFI-safe
34+
//~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
35+
extern "C" fn rust_no_special_help_for_mut_cstring(s: *mut CString) {}
36+
extern "C" fn rust_no_special_help_for_mut_cstring_ref(s: &mut CString) {}

tests/ui/lint/lint-ctypes-cstr.stderr

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
error: `extern` block uses type `CStr`, which is not FFI-safe
2+
--> $DIR/lint-ctypes-cstr.rs:7:21
3+
|
4+
LL | fn take_cstr(s: CStr);
5+
| ^^^^ not FFI-safe
6+
|
7+
= help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
8+
= note: `CStr`/`CString` do not have a guaranteed layout
9+
note: the lint level is defined here
10+
--> $DIR/lint-ctypes-cstr.rs:2:9
11+
|
12+
LL | #![deny(improper_ctypes, improper_ctypes_definitions)]
13+
| ^^^^^^^^^^^^^^^
14+
15+
error: `extern` block uses type `CStr`, which is not FFI-safe
16+
--> $DIR/lint-ctypes-cstr.rs:10:25
17+
|
18+
LL | fn take_cstr_ref(s: &CStr);
19+
| ^^^^^ not FFI-safe
20+
|
21+
= help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
22+
= note: `CStr`/`CString` do not have a guaranteed layout
23+
24+
error: `extern` block uses type `CString`, which is not FFI-safe
25+
--> $DIR/lint-ctypes-cstr.rs:13:24
26+
|
27+
LL | fn take_cstring(s: CString);
28+
| ^^^^^^^ not FFI-safe
29+
|
30+
= help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
31+
= note: `CStr`/`CString` do not have a guaranteed layout
32+
33+
error: `extern` block uses type `CString`, which is not FFI-safe
34+
--> $DIR/lint-ctypes-cstr.rs:16:28
35+
|
36+
LL | fn take_cstring_ref(s: &CString);
37+
| ^^^^^^^^ not FFI-safe
38+
|
39+
= help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
40+
= note: `CStr`/`CString` do not have a guaranteed layout
41+
42+
error: `extern` block uses type `CString`, which is not FFI-safe
43+
--> $DIR/lint-ctypes-cstr.rs:20:43
44+
|
45+
LL | fn no_special_help_for_mut_cstring(s: *mut CString);
46+
| ^^^^^^^^^^^^ not FFI-safe
47+
|
48+
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
49+
= note: this struct has unspecified layout
50+
51+
error: `extern` block uses type `CString`, which is not FFI-safe
52+
--> $DIR/lint-ctypes-cstr.rs:24:47
53+
|
54+
LL | fn no_special_help_for_mut_cstring_ref(s: &mut CString);
55+
| ^^^^^^^^^^^^ not FFI-safe
56+
|
57+
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
58+
= note: this struct has unspecified layout
59+
60+
error: `extern` fn uses type `CStr`, which is not FFI-safe
61+
--> $DIR/lint-ctypes-cstr.rs:29:37
62+
|
63+
LL | extern "C" fn rust_take_cstr_ref(s: &CStr) {}
64+
| ^^^^^ not FFI-safe
65+
|
66+
= help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
67+
= note: `CStr`/`CString` do not have a guaranteed layout
68+
note: the lint level is defined here
69+
--> $DIR/lint-ctypes-cstr.rs:2:26
70+
|
71+
LL | #![deny(improper_ctypes, improper_ctypes_definitions)]
72+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
73+
74+
error: `extern` fn uses type `CString`, which is not FFI-safe
75+
--> $DIR/lint-ctypes-cstr.rs:32:36
76+
|
77+
LL | extern "C" fn rust_take_cstring(s: CString) {}
78+
| ^^^^^^^ not FFI-safe
79+
|
80+
= help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
81+
= note: `CStr`/`CString` do not have a guaranteed layout
82+
83+
error: aborting due to 8 previous errors
84+

0 commit comments

Comments
 (0)