Skip to content

Commit 24f90fd

Browse files
committed
lint/ctypes: allow () within types
Consider `()` within types to be FFI-safe, and `()` to be FFI-safe as a return type (incl. when in a transparent newtype). Signed-off-by: David Wood <[email protected]>
1 parent 99b1897 commit 24f90fd

7 files changed

+87
-83
lines changed

compiler/rustc_lint/src/types.rs

+15-32
Original file line numberDiff line numberDiff line change
@@ -943,30 +943,6 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
943943
}
944944

945945
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
946-
// Returns `true` if `ty` is a `()`, or a `repr(transparent)` type whose only non-ZST field
947-
// is a generic substituted for `()` - in either case, the type is FFI-safe when used as a
948-
// return type.
949-
pub fn is_unit_or_equivalent(&self, ty: Ty<'tcx>) -> bool {
950-
if ty.is_unit() {
951-
return true;
952-
}
953-
954-
if let ty::Adt(def, substs) = ty.kind() && def.repr().transparent() {
955-
return def.variants()
956-
.iter()
957-
.filter_map(|variant| transparent_newtype_field(self.cx.tcx, variant))
958-
.all(|field| {
959-
let field_ty = field.ty(self.cx.tcx, substs);
960-
!field_ty.has_opaque_types() && {
961-
let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty);
962-
self.is_unit_or_equivalent(field_ty)
963-
}
964-
});
965-
}
966-
967-
false
968-
}
969-
970946
/// Check if the type is array and emit an unsafe type lint.
971947
fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
972948
if let ty::Array(..) = ty.kind() {
@@ -1010,14 +986,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1010986
use FfiResult::*;
1011987

1012988
let transparent_with_all_zst_fields = if def.repr().transparent() {
1013-
// Transparent newtypes have at most one non-ZST field which needs to be checked..
1014989
if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
1015-
return self.check_field_type_for_ffi(cache, field, args);
1016-
}
990+
// Transparent newtypes have at most one non-ZST field which needs to be checked..
991+
match self.check_field_type_for_ffi(cache, field, args) {
992+
FfiUnsafe { ty, .. } if ty.is_unit() => (),
993+
r => return r,
994+
}
1017995

1018-
// ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
1019-
// `PhantomData`).
1020-
true
996+
false
997+
} else {
998+
// ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
999+
// `PhantomData`).
1000+
true
1001+
}
10211002
} else {
10221003
false
10231004
};
@@ -1027,6 +1008,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10271008
for field in &variant.fields {
10281009
all_phantom &= match self.check_field_type_for_ffi(cache, &field, args) {
10291010
FfiSafe => false,
1011+
// `()` fields are FFI-safe!
1012+
FfiUnsafe { ty, .. } if ty.is_unit() => false,
10301013
FfiPhantom(..) => true,
10311014
r @ FfiUnsafe { .. } => return r,
10321015
}
@@ -1249,7 +1232,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12491232
}
12501233

12511234
let ret_ty = sig.output();
1252-
if self.is_unit_or_equivalent(ret_ty) {
1235+
if ret_ty.is_unit() {
12531236
return FfiSafe;
12541237
}
12551238

@@ -1374,7 +1357,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13741357
// Don't report FFI errors for unit return types. This check exists here, and not in
13751358
// the caller (where it would make more sense) so that normalization has definitely
13761359
// happened.
1377-
if is_return_type && self.is_unit_or_equivalent(ty) {
1360+
if is_return_type && ty.is_unit() {
13781361
return;
13791362
}
13801363

tests/ui/lint/lint-ctypes-113436-1.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#![deny(improper_ctypes_definitions)]
2+
3+
#[repr(C)]
4+
pub struct Foo {
5+
a: u8,
6+
b: (),
7+
}
8+
9+
extern "C" fn foo(x: Foo) -> Foo {
10+
todo!()
11+
}
12+
13+
struct NotSafe(u32);
14+
15+
#[repr(C)]
16+
pub struct Bar {
17+
a: u8,
18+
b: (),
19+
c: NotSafe,
20+
}
21+
22+
extern "C" fn bar(x: Bar) -> Bar {
23+
//~^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe
24+
//~^^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe
25+
todo!()
26+
}
27+
28+
fn main() {}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: `extern` fn uses type `NotSafe`, which is not FFI-safe
2+
--> $DIR/lint-ctypes-113436-1.rs:22:22
3+
|
4+
LL | extern "C" fn bar(x: Bar) -> Bar {
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-113436-1.rs:13:1
11+
|
12+
LL | struct NotSafe(u32);
13+
| ^^^^^^^^^^^^^^
14+
note: the lint level is defined here
15+
--> $DIR/lint-ctypes-113436-1.rs:1:9
16+
|
17+
LL | #![deny(improper_ctypes_definitions)]
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
20+
error: `extern` fn uses type `NotSafe`, which is not FFI-safe
21+
--> $DIR/lint-ctypes-113436-1.rs:22:30
22+
|
23+
LL | extern "C" fn bar(x: Bar) -> Bar {
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-113436-1.rs:13:1
30+
|
31+
LL | struct NotSafe(u32);
32+
| ^^^^^^^^^^^^^^
33+
34+
error: aborting due to 2 previous errors
35+

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

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// check-pass
12
#![deny(improper_ctypes_definitions)]
23

34
#[repr(C)]
@@ -7,20 +8,16 @@ pub struct Wrap<T>(T);
78
pub struct TransparentWrap<T>(T);
89

910
pub extern "C" fn f() -> Wrap<()> {
10-
//~^ ERROR `extern` fn uses type `()`, which is not FFI-safe
1111
todo!()
1212
}
1313

1414
const _: extern "C" fn() -> Wrap<()> = f;
15-
//~^ ERROR `extern` fn uses type `()`, which is not FFI-safe
1615

1716
pub extern "C" fn ff() -> Wrap<Wrap<()>> {
18-
//~^ ERROR `extern` fn uses type `()`, which is not FFI-safe
1917
todo!()
2018
}
2119

2220
const _: extern "C" fn() -> Wrap<Wrap<()>> = ff;
23-
//~^ ERROR `extern` fn uses type `()`, which is not FFI-safe
2421

2522
pub extern "C" fn g() -> TransparentWrap<()> {
2623
todo!()

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

-43
This file was deleted.

tests/ui/repr/repr-transparent-issue-87496.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
struct TransparentCustomZst(());
77
extern "C" {
88
fn good17(p: TransparentCustomZst);
9-
//~^ WARNING: `extern` block uses type `()`, which is not FFI-safe
9+
//~^ WARNING: `extern` block uses type `TransparentCustomZst`, which is not FFI-safe
1010
}
1111

1212
fn main() {}

tests/ui/repr/repr-transparent-issue-87496.stderr

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
warning: `extern` block uses type `()`, which is not FFI-safe
1+
warning: `extern` block uses type `TransparentCustomZst`, which is not FFI-safe
22
--> $DIR/repr-transparent-issue-87496.rs:8:18
33
|
44
LL | fn good17(p: TransparentCustomZst);
55
| ^^^^^^^^^^^^^^^^^^^^ not FFI-safe
66
|
7-
= help: consider using a struct instead
8-
= note: tuples have unspecified layout
7+
= note: this struct contains only zero-sized fields
8+
note: the type is defined here
9+
--> $DIR/repr-transparent-issue-87496.rs:6:1
10+
|
11+
LL | struct TransparentCustomZst(());
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
913
= note: `#[warn(improper_ctypes)]` on by default
1014

1115
warning: 1 warning emitted

0 commit comments

Comments
 (0)