Skip to content

Commit 62aa8f0

Browse files
compiler: Embed consensus in lint::types::improper_ctypes
Extracting this logic into a module makes it easier to write down, and more importantly, later find, the actual decisions we've made.
1 parent e9cf280 commit 62aa8f0

File tree

2 files changed

+65
-28
lines changed

2 files changed

+65
-28
lines changed

Diff for: compiler/rustc_lint/src/types.rs

+14-28
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::ops::ControlFlow;
33

44
use rustc_data_structures::fx::FxHashSet;
55
use rustc_errors::DiagMessage;
6-
use rustc_hir::def::CtorKind;
76
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
87
use rustc_middle::bug;
98
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
@@ -19,6 +18,8 @@ use rustc_target::spec::abi::Abi as SpecAbi;
1918
use tracing::debug;
2019
use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
2120

21+
mod improper_ctypes;
22+
2223
use crate::lints::{
2324
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
2425
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
@@ -1405,38 +1406,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14051406
};
14061407
}
14071408

1408-
// non_exhaustive suggests it is possible that someone might break ABI
1409-
// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344
1410-
// so warn on complex enums being used outside their crate
1411-
let nonexhaustive_nonlocal_ffi =
1412-
def.is_variant_list_non_exhaustive() && !def.did().is_local();
1409+
use improper_ctypes::{
1410+
check_non_exhaustive_variant, non_local_and_non_exhaustive,
1411+
};
14131412

1413+
let non_local_def = non_local_and_non_exhaustive(def);
14141414
// Check the contained variants.
1415-
for variant in def.variants() {
1416-
// but only warn about really_tagged_union reprs,
1417-
// exempt enums with unit ctors like C's (like rust-bindgen)
1418-
if nonexhaustive_nonlocal_ffi
1419-
&& !matches!(variant.ctor_kind(), Some(CtorKind::Const))
1420-
{
1421-
return FfiUnsafe {
1422-
ty,
1423-
reason: fluent::lint_improper_ctypes_non_exhaustive,
1424-
help: None,
1425-
};
1426-
};
1427-
let is_non_exhaustive = variant.is_field_list_non_exhaustive();
1428-
if is_non_exhaustive && !variant.def_id.is_local() {
1429-
return FfiUnsafe {
1430-
ty,
1431-
reason: fluent::lint_improper_ctypes_non_exhaustive_variant,
1432-
help: None,
1433-
};
1434-
}
1415+
let ret = def.variants().iter().try_for_each(|variant| {
1416+
check_non_exhaustive_variant(non_local_def, variant)
1417+
.map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
14351418

14361419
match self.check_variant_for_ffi(acc, ty, def, variant, args) {
1437-
FfiSafe => (),
1438-
r => return r,
1420+
FfiSafe => ControlFlow::Continue(()),
1421+
r => ControlFlow::Break(r),
14391422
}
1423+
});
1424+
if let ControlFlow::Break(result) = ret {
1425+
return result;
14401426
}
14411427

14421428
FfiSafe

Diff for: compiler/rustc_lint/src/types/improper_ctypes.rs

+51
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+
}

0 commit comments

Comments
 (0)