Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 91e377c

Browse files
davidtwcocuviper
authored andcommitted
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 <david@davidtw.co> (cherry picked from commit 24f90fd)
1 parent e9cd650 commit 91e377c

File tree

7 files changed

+87
-83
lines changed

7 files changed

+87
-83
lines changed

compiler/rustc_lint/src/types.rs

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -940,30 +940,6 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
940940
}
941941

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

1009985
let transparent_with_all_zst_fields = if def.repr().transparent() {
1010-
// Transparent newtypes have at most one non-ZST field which needs to be checked..
1011986
if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
1012-
return self.check_field_type_for_ffi(cache, field, substs);
1013-
}
987+
// Transparent newtypes have at most one non-ZST field which needs to be checked..
988+
match self.check_field_type_for_ffi(cache, field, substs) {
989+
FfiUnsafe { ty, .. } if ty.is_unit() => (),
990+
r => return r,
991+
}
1014992

1015-
// ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
1016-
// `PhantomData`).
1017-
true
993+
false
994+
} else {
995+
// ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
996+
// `PhantomData`).
997+
true
998+
}
1018999
} else {
10191000
false
10201001
};
@@ -1024,6 +1005,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10241005
for field in &variant.fields {
10251006
all_phantom &= match self.check_field_type_for_ffi(cache, &field, substs) {
10261007
FfiSafe => false,
1008+
// `()` fields are FFI-safe!
1009+
FfiUnsafe { ty, .. } if ty.is_unit() => false,
10271010
FfiPhantom(..) => true,
10281011
r @ FfiUnsafe { .. } => return r,
10291012
}
@@ -1246,7 +1229,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12461229
}
12471230

12481231
let ret_ty = sig.output();
1249-
if self.is_unit_or_equivalent(ret_ty) {
1232+
if ret_ty.is_unit() {
12501233
return FfiSafe;
12511234
}
12521235

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

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

Lines changed: 28 additions & 0 deletions
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() {}
Lines changed: 35 additions & 0 deletions
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

Lines changed: 1 addition & 4 deletions
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

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

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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 7 additions & 3 deletions
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)