Skip to content

Commit 231244c

Browse files
authored
Unrolled build for rust-lang#116863
Rollup merge of rust-lang#116863 - workingjubilee:non-exhaustive-is-not-ffi-unsafe, r=jieyouxu warn less about non-exhaustive in ffi Bindgen allows generating `#[non_exhaustive] #[repr(u32)]` enums. This results in nonintuitive nonlocal `improper_ctypes` warnings, even when the types are otherwise perfectly valid in C. Adjust for actual tooling expectations by avoiding warning on simple enums with only unit variants. Closes rust-lang#116831
2 parents da93539 + 62aa8f0 commit 231244c

File tree

5 files changed

+91
-26
lines changed

5 files changed

+91
-26
lines changed

compiler/rustc_lint/src/types.rs

+15-20
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use rustc_target::spec::abi::Abi as SpecAbi;
1818
use tracing::debug;
1919
use {rustc_ast as ast, rustc_hir as hir};
2020

21+
mod improper_ctypes;
22+
2123
use crate::lints::{
2224
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
2325
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
@@ -983,15 +985,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
983985
// Empty enums are okay... although sort of useless.
984986
return FfiSafe;
985987
}
986-
987-
if def.is_variant_list_non_exhaustive() && !def.did().is_local() {
988-
return FfiUnsafe {
989-
ty,
990-
reason: fluent::lint_improper_ctypes_non_exhaustive,
991-
help: None,
992-
};
993-
}
994-
995988
// Check for a repr() attribute to specify the size of the
996989
// discriminant.
997990
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
@@ -1010,21 +1003,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10101003
};
10111004
}
10121005

1006+
use improper_ctypes::{
1007+
check_non_exhaustive_variant, non_local_and_non_exhaustive,
1008+
};
1009+
1010+
let non_local_def = non_local_and_non_exhaustive(def);
10131011
// Check the contained variants.
1014-
for variant in def.variants() {
1015-
let is_non_exhaustive = variant.is_field_list_non_exhaustive();
1016-
if is_non_exhaustive && !variant.def_id.is_local() {
1017-
return FfiUnsafe {
1018-
ty,
1019-
reason: fluent::lint_improper_ctypes_non_exhaustive_variant,
1020-
help: None,
1021-
};
1022-
}
1012+
let ret = def.variants().iter().try_for_each(|variant| {
1013+
check_non_exhaustive_variant(non_local_def, variant)
1014+
.map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
10231015

10241016
match self.check_variant_for_ffi(acc, ty, def, variant, args) {
1025-
FfiSafe => (),
1026-
r => return r,
1017+
FfiSafe => ControlFlow::Continue(()),
1018+
r => ControlFlow::Break(r),
10271019
}
1020+
});
1021+
if let ControlFlow::Break(result) = ret {
1022+
return result;
10281023
}
10291024

10301025
FfiSafe
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use std::ops::ControlFlow;
2+
3+
use rustc_errors::DiagMessage;
4+
use rustc_hir::def::CtorKind;
5+
use rustc_middle::ty;
6+
7+
use crate::fluent_generated as fluent;
8+
9+
/// Check a variant of a non-exhaustive enum for improper ctypes
10+
///
11+
/// We treat `#[non_exhaustive] enum` as "ensure that code will compile if new variants are added".
12+
/// This includes linting, on a best-effort basis. There are valid additions that are unlikely.
13+
///
14+
/// Adding a data-carrying variant to an existing C-like enum that is passed to C is "unlikely",
15+
/// so we don't need the lint to account for it.
16+
/// e.g. going from enum Foo { A, B, C } to enum Foo { A, B, C, D(u32) }.
17+
pub(crate) fn check_non_exhaustive_variant(
18+
non_local_def: bool,
19+
variant: &ty::VariantDef,
20+
) -> ControlFlow<DiagMessage, ()> {
21+
// non_exhaustive suggests it is possible that someone might break ABI
22+
// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344
23+
// so warn on complex enums being used outside their crate
24+
if non_local_def {
25+
// which is why we only warn about really_tagged_union reprs from https://rust.tf/rfc2195
26+
// with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }`
27+
// but exempt enums with unit ctors like C's (e.g. from rust-bindgen)
28+
if variant_has_complex_ctor(variant) {
29+
return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive);
30+
}
31+
}
32+
33+
let non_exhaustive_variant_fields = variant.is_field_list_non_exhaustive();
34+
if non_exhaustive_variant_fields && !variant.def_id.is_local() {
35+
return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant);
36+
}
37+
38+
ControlFlow::Continue(())
39+
}
40+
41+
fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool {
42+
// CtorKind::Const means a "unit" ctor
43+
!matches!(variant.ctor_kind(), Some(CtorKind::Const))
44+
}
45+
46+
// non_exhaustive suggests it is possible that someone might break ABI
47+
// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344
48+
// so warn on complex enums being used outside their crate
49+
pub(crate) fn non_local_and_non_exhaustive(def: ty::AdtDef<'_>) -> bool {
50+
def.is_variant_list_non_exhaustive() && !def.did().is_local()
51+
}

tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs

+11
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,14 @@ pub enum NonExhaustiveVariants {
2727
#[non_exhaustive] Tuple(u32),
2828
#[non_exhaustive] Struct { field: u32 }
2929
}
30+
31+
// Note the absence of repr(C): it's not necessary, and recent C code can now use repr hints too.
32+
#[repr(u32)]
33+
#[non_exhaustive]
34+
pub enum NonExhaustiveCLikeEnum {
35+
One = 1,
36+
Two = 2,
37+
Three = 3,
38+
Four = 4,
39+
Five = 5,
40+
}

tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ extern crate types;
66
// This test checks that non-exhaustive types with `#[repr(C)]` from an extern crate are considered
77
// improper.
88

9-
use types::{NonExhaustiveEnum, NonExhaustiveVariants, NormalStruct, TupleStruct, UnitStruct};
9+
use types::{
10+
NonExhaustiveCLikeEnum, NonExhaustiveEnum, NonExhaustiveVariants,
11+
NormalStruct, TupleStruct, UnitStruct,
12+
};
1013

1114
extern "C" {
1215
pub fn non_exhaustive_enum(_: NonExhaustiveEnum);
@@ -21,4 +24,9 @@ extern "C" {
2124
//~^ ERROR `extern` block uses type `NonExhaustiveVariants`, which is not FFI-safe
2225
}
2326

27+
// These should pass without remark, as they're C-compatible, despite being "non-exhaustive".
28+
extern "C" {
29+
pub fn non_exhaustive_c_compat_enum(_: NonExhaustiveCLikeEnum);
30+
}
31+
2432
fn main() {}

tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: `extern` block uses type `NonExhaustiveEnum`, which is not FFI-safe
2-
--> $DIR/extern_crate_improper.rs:12:35
2+
--> $DIR/extern_crate_improper.rs:15:35
33
|
44
LL | pub fn non_exhaustive_enum(_: NonExhaustiveEnum);
55
| ^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -12,31 +12,31 @@ LL | #![deny(improper_ctypes)]
1212
| ^^^^^^^^^^^^^^^
1313

1414
error: `extern` block uses type `NormalStruct`, which is not FFI-safe
15-
--> $DIR/extern_crate_improper.rs:14:44
15+
--> $DIR/extern_crate_improper.rs:17:44
1616
|
1717
LL | pub fn non_exhaustive_normal_struct(_: NormalStruct);
1818
| ^^^^^^^^^^^^ not FFI-safe
1919
|
2020
= note: this struct is non-exhaustive
2121

2222
error: `extern` block uses type `UnitStruct`, which is not FFI-safe
23-
--> $DIR/extern_crate_improper.rs:16:42
23+
--> $DIR/extern_crate_improper.rs:19:42
2424
|
2525
LL | pub fn non_exhaustive_unit_struct(_: UnitStruct);
2626
| ^^^^^^^^^^ not FFI-safe
2727
|
2828
= note: this struct is non-exhaustive
2929

3030
error: `extern` block uses type `TupleStruct`, which is not FFI-safe
31-
--> $DIR/extern_crate_improper.rs:18:43
31+
--> $DIR/extern_crate_improper.rs:21:43
3232
|
3333
LL | pub fn non_exhaustive_tuple_struct(_: TupleStruct);
3434
| ^^^^^^^^^^^ not FFI-safe
3535
|
3636
= note: this struct is non-exhaustive
3737

3838
error: `extern` block uses type `NonExhaustiveVariants`, which is not FFI-safe
39-
--> $DIR/extern_crate_improper.rs:20:38
39+
--> $DIR/extern_crate_improper.rs:23:38
4040
|
4141
LL | pub fn non_exhaustive_variant(_: NonExhaustiveVariants);
4242
| ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe

0 commit comments

Comments
 (0)