Skip to content

Commit 4aa6a20

Browse files
committed
lint ImproperCTypes: smoothing out some nits and un-tidy-ness
1 parent b27bb61 commit 4aa6a20

File tree

5 files changed

+88
-126
lines changed

5 files changed

+88
-126
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,13 +426,13 @@ lint_improper_ctypes_ptr_validity_reason =
426426
lint_improper_ctypes_sized_ptr_to_unsafe_type =
427427
this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout
428428
429-
lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field
430429
lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead
431-
432430
lint_improper_ctypes_slice_reason = slices have no C equivalent
433-
lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
434431
432+
lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
435433
lint_improper_ctypes_str_reason = string slices have no C equivalent
434+
435+
lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field
436436
lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct
437437
438438
lint_improper_ctypes_struct_fieldless_reason = this struct has no fields

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 63 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -148,61 +148,25 @@ impl<'tcx> FfiResult<'tcx> {
148148
}
149149

150150
impl<'tcx> std::ops::AddAssign<FfiResult<'tcx>> for FfiResult<'tcx> {
151-
fn add_assign(&mut self, mut other: Self) {
151+
fn add_assign(&mut self, other: Self) {
152152
// note: we shouldn't really encounter FfiPhantoms here, they should be dealt with beforehand
153153
// still, this function deals with them in a reasonable way, I think
154154

155-
// this function is awful to look but that's because matching mutable references consumes them (?!)
156-
// the function itself imitates the following piece of non-compiling code:
157-
158-
// match (self, other) {
159-
// (Self::FfiUnsafe(_), _) => {
160-
// // nothing to do
161-
// },
162-
// (_, Self::FfiUnsafe(_)) => {
163-
// *self = other;
164-
// },
165-
// (Self::FfiPhantom(ref ty1),Self::FfiPhantom(ty2)) => {
166-
// println!("whoops, both FfiPhantom: self({:?}) += other({:?})", ty1, ty2);
167-
// },
168-
// (Self::FfiSafe,Self::FfiPhantom(_)) => {
169-
// *self = other;
170-
// },
171-
// (_, Self::FfiSafe) => {
172-
// // nothing to do
173-
// },
174-
// }
175-
176-
let s_disc = std::mem::discriminant(self);
177-
let o_disc = std::mem::discriminant(&other);
178-
if s_disc == o_disc {
179-
match (self, &mut other) {
180-
(Self::FfiUnsafe(s_inner), Self::FfiUnsafe(o_inner)) => {
181-
s_inner.append(o_inner);
182-
}
183-
(Self::FfiPhantom(ty1), Self::FfiPhantom(ty2)) => {
184-
debug!("whoops: both FfiPhantom, self({:?}) += other({:?})", ty1, ty2);
185-
}
186-
(Self::FfiSafe, Self::FfiSafe) => {}
187-
_ => unreachable!(),
155+
match (self, other) {
156+
(Self::FfiUnsafe(_), _) => {
157+
// nothing to do
188158
}
189-
} else {
190-
if let Self::FfiUnsafe(_) = self {
191-
return;
159+
(myself, other @ Self::FfiUnsafe(_)) => {
160+
*myself = other;
192161
}
193-
match other {
194-
Self::FfiUnsafe(o_inner) => {
195-
// self is Safe or Phantom: Unsafe wins
196-
*self = Self::FfiUnsafe(o_inner);
197-
}
198-
Self::FfiSafe => {
199-
// self is always "wins"
200-
return;
201-
}
202-
Self::FfiPhantom(o_inner) => {
203-
// self is Safe: Phantom wins
204-
*self = Self::FfiPhantom(o_inner);
205-
}
162+
(Self::FfiPhantom(ty1), Self::FfiPhantom(ty2)) => {
163+
debug!("whoops, both FfiPhantom: self({:?}) += other({:?})", ty1, ty2);
164+
}
165+
(myself @ Self::FfiSafe, other @ Self::FfiPhantom(_)) => {
166+
*myself = other;
167+
}
168+
(_, Self::FfiSafe) => {
169+
// nothing to do
206170
}
207171
}
208172
}
@@ -215,7 +179,7 @@ impl<'tcx> std::ops::Add<FfiResult<'tcx>> for FfiResult<'tcx> {
215179
}
216180
}
217181

218-
/// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it
182+
/// Determine if a type is sized or not, and whether it affects references/pointers/boxes to it
219183
#[derive(Clone, Copy)]
220184
enum TypeSizedness {
221185
/// type of definite size (pointers are C-compatible)
@@ -354,51 +318,63 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
354318
}
355319
}
356320

321+
#[allow(non_snake_case)]
322+
mod CTypesVisitorStateFlags {
323+
pub(super) const NO_FLAGS: u8 = 0b0000;
324+
/// for static variables (not used in functions)
325+
pub(super) const STATIC: u8 = 0b0010;
326+
/// for variables in function returns (implicitly: not for static variables)
327+
pub(super) const FN_RETURN: u8 = 0b0100;
328+
/// for variables in functions which are defined in rust (implicitly: not for static variables)
329+
pub(super) const FN_DECLARED: u8 = 0b1000;
330+
}
331+
357332
#[repr(u8)]
358333
#[derive(Clone, Copy, Debug)]
359334
enum CTypesVisitorState {
360-
// bitflags:
361-
// 0010: static
362-
// 0100: function return
363-
// 1000: used in declared function
364-
StaticTy = 0b0010,
365-
ArgumentTyInDefinition = 0b1000,
366-
ReturnTyInDefinition = 0b1100,
367-
ArgumentTyInDeclaration = 0b0000,
368-
ReturnTyInDeclaration = 0b0100,
335+
// uses bitflags from CTypesVisitorStateFlags
336+
StaticTy = CTypesVisitorStateFlags::STATIC,
337+
ArgumentTyInDefinition = CTypesVisitorStateFlags::FN_DECLARED,
338+
ReturnTyInDefinition =
339+
CTypesVisitorStateFlags::FN_DECLARED | CTypesVisitorStateFlags::FN_RETURN,
340+
ArgumentTyInDeclaration = CTypesVisitorStateFlags::NO_FLAGS,
341+
ReturnTyInDeclaration = CTypesVisitorStateFlags::FN_RETURN,
369342
}
370343

371344
impl CTypesVisitorState {
372-
/// wether the type is used (directly or not) in a static variable
345+
/// whether the type is used (directly or not) in a static variable
373346
fn is_in_static(self) -> bool {
374-
((self as u8) & 0b0010) != 0
347+
use CTypesVisitorStateFlags::*;
348+
((self as u8) & STATIC) != 0
375349
}
376-
/// wether the type is used (directly or not) in a function, in return position
350+
/// whether the type is used (directly or not) in a function, in return position
377351
fn is_in_function_return(self) -> bool {
378-
let ret = ((self as u8) & 0b0100) != 0;
352+
use CTypesVisitorStateFlags::*;
353+
let ret = ((self as u8) & FN_RETURN) != 0;
379354
#[cfg(debug_assertions)]
380355
if ret {
381356
assert!(!self.is_in_static());
382357
}
383358
ret
384359
}
385-
/// wether the type is used (directly or not) in a defined function
386-
/// in other words, wether or not we allow non-FFI-safe types behind a C pointer,
360+
/// whether the type is used (directly or not) in a defined function
361+
/// in other words, whether or not we allow non-FFI-safe types behind a C pointer,
387362
/// to be treated as an opaque type on the other side of the FFI boundary
388363
fn is_in_defined_function(self) -> bool {
389-
let ret = ((self as u8) & 0b1000) != 0;
364+
use CTypesVisitorStateFlags::*;
365+
let ret = ((self as u8) & FN_DECLARED) != 0;
390366
#[cfg(debug_assertions)]
391367
if ret {
392368
assert!(!self.is_in_static());
393369
}
394370
ret
395371
}
396372

397-
/// wether the value for that type might come from the non-rust side of a FFI boundary
373+
/// whether the value for that type might come from the non-rust side of a FFI boundary
398374
fn value_may_be_unchecked(self) -> bool {
399375
// function declarations are assumed to be rust-caller, non-rust-callee
400376
// function definitions are assumed to be maybe-not-rust-caller, rust-callee
401-
// FnPtrs are... well, nothing's certain about anything. (TODO need more flags in enum?)
377+
// FnPtrs are... well, nothing's certain about anything. (FIXME need more flags in enum?)
402378
// Same with statics.
403379
if self.is_in_static() {
404380
true
@@ -411,7 +387,7 @@ impl CTypesVisitorState {
411387
}
412388

413389
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
414-
/// Checks wether an `extern "ABI" fn` function pointer is indeed FFI-safe to call
390+
/// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call
415391
fn visit_fnptr(&mut self, mode: CItemKind, ty: Ty<'tcx>, sig: Sig<'tcx>) -> FfiResult<'tcx> {
416392
use FfiResult::*;
417393
debug_assert!(!sig.abi().is_rustic_abi());
@@ -534,7 +510,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
534510
// there are three remaining concerns with the pointer:
535511
// - is the pointer compatible with a C pointer in the first place? (if not, only send that error message)
536512
// - is the pointee FFI-safe? (it might not matter, see mere lines below)
537-
// - does the pointer type contain a non-zero assumption, but a value given by non-rust code?
513+
// - does the pointer type contain a non-zero assumption, but has a value given by non-rust code?
538514
// this block deals with the first two.
539515
let mut ffi_res = match get_type_sizedness(self.cx, inner_ty) {
540516
TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => {
@@ -583,7 +559,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
583559
// 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions.
584560
// (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't)
585561

586-
// wether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box<impl Trait>),
562+
// whether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box<impl Trait>),
587563
// so let's not wrap the current context around a potential FfiUnsafe type param.
588564
self.visit_type(state, Some(ty), inner_ty)
589565
}
@@ -603,6 +579,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
603579
};
604580

605581
// and now the third concern (does the pointer type contain a non-zero assumption, and is the value given by non-rust code?)
582+
// technically, pointers with non-rust-given values could also be misaligned, pointing to the wrong thing, or outright dangling, but we assume they never are
606583
ffi_res += if state.value_may_be_unchecked() {
607584
let has_nonnull_assumption = match indirection_type {
608585
IndirectionType::RawPtr => false,
@@ -758,18 +735,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
758735

759736
if def.variants().is_empty() {
760737
// Empty enums are implicitely handled as the never type:
761-
// TODO think about the FFI-safety of functions that use that
738+
// FIXME think about the FFI-safety of functions that use that
762739
return FfiSafe;
763740
}
764741
// Check for a repr() attribute to specify the size of the
765742
// discriminant.
766743
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() {
767744
// Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
768-
if let Some(inner_ty) = repr_nullable_ptr(
769-
self.cx.tcx,
770-
self.cx.typing_env(),
771-
ty,
772-
) {
745+
if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) {
773746
return self.visit_type(state, Some(ty), inner_ty);
774747
}
775748

@@ -781,11 +754,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
781754
}
782755

783756
if let Some(IntegerType::Fixed(Integer::I128, _)) = def.repr().int {
784-
return FfiResult::new_with_reason(
785-
ty,
786-
fluent::lint_improper_ctypes_128bit,
787-
None,
788-
);
757+
return FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_128bit, None);
789758
}
790759

791760
let non_exhaustive = def.variant_list_has_applicable_non_exhaustive();
@@ -811,7 +780,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
811780
.iter()
812781
.map(|variant| {
813782
self.visit_variant_fields(state, ty, def, variant, args)
814-
// TODO: check that enums allow any (up to all) variants to be phantoms?
783+
// FIXME: check that enums allow any (up to all) variants to be phantoms?
815784
// (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
816785
.forbid_phantom()
817786
})
@@ -856,11 +825,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
856825
}
857826
match def.adt_kind() {
858827
AdtKind::Struct | AdtKind::Union => {
859-
// I thought CStr (not CString) could not be reached here:
860-
// - not using an indirection would cause a compile error prior to this lint
828+
// I thought CStr (not CString) here could only be reached in non-compiling code:
829+
// - not using an indirection would cause a compile error (this lint *currently* seems to not get triggered on such non-compiling code)
861830
// - and using one would cause the lint to catch on the indirection before reaching its pointee
862-
// but for some reason one can just go and write function *pointers* like that:
863-
// `type Foo = extern "C" fn(::std::ffi::CStr);`
831+
// but function *pointers* don't seem to have the same no-unsized-parameters requirement to compile
864832
if let Some(sym::cstring_type | sym::cstr_type) =
865833
tcx.get_diagnostic_name(def.did())
866834
{
@@ -1107,10 +1075,7 @@ struct ImproperCTypesLint<'c, 'tcx> {
11071075
}
11081076

11091077
impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
1110-
fn check_arg_for_power_alignment(
1111-
&mut self,
1112-
ty: Ty<'tcx>,
1113-
) -> bool {
1078+
fn check_arg_for_power_alignment(&mut self, ty: Ty<'tcx>) -> bool {
11141079
let tcx = self.cx.tcx;
11151080
assert!(tcx.sess.target.os == "aix");
11161081
// Structs (under repr(C)) follow the power alignment rule if:
@@ -1141,10 +1106,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
11411106
return false;
11421107
}
11431108

1144-
fn check_struct_for_power_alignment(
1145-
&mut self,
1146-
item: &'tcx hir::Item<'tcx>,
1147-
) {
1109+
fn check_struct_for_power_alignment(&mut self, item: &'tcx hir::Item<'tcx>) {
11481110
let tcx = self.cx.tcx;
11491111
let adt_def = tcx.adt_def(item.owner_id.to_def_id());
11501112
// repr(C) structs also with packed or aligned representation
@@ -1228,7 +1190,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
12281190
(span, ffi_res)
12291191
})
12301192
// even in function *definitions*, `FnPtr`s are always function declarations ...right?
1231-
// (TODO: we can't do that yet because one of rustc's crates can't compile if we do)
1193+
// (FIXME: we can't do that yet because one of rustc's crates can't compile if we do)
12321194
.for_each(|(span, ffi_res)| self.process_ffi_result(span, ffi_res, fn_mode));
12331195
//.drain();
12341196
}
@@ -1316,12 +1278,8 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
13161278

13171279
// this whole while block converts the arbitrarily-deep
13181280
// FfiResult stack to an ImproperCTypesLayer Vec
1319-
while let ControlFlow::Continue(FfiUnsafeReason {
1320-
ty,
1321-
reason,
1322-
help,
1323-
inner,
1324-
}) = ffiresult_recursor
1281+
while let ControlFlow::Continue(FfiUnsafeReason { ty, reason, help, inner }) =
1282+
ffiresult_recursor
13251283
{
13261284
if let Some(layer) = cimproper_layers.last_mut() {
13271285
layer.inner_ty = Some(ty.clone());
@@ -1384,7 +1342,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
13841342

13851343
match it.kind {
13861344
hir::ForeignItemKind::Fn(sig, _, _) => {
1387-
if abi.is_rustic_abi() {
1345+
if abi.is_rustic_abi() {
13881346
lint.check_fn_for_external_abi_fnptr(
13891347
CItemKind::Declaration,
13901348
it.owner_id.def_id,
@@ -1426,7 +1384,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
14261384
// Structs are checked based on if they follow the power alignment
14271385
// rule (under repr(C)).
14281386
hir::ItemKind::Struct(..) => {
1429-
ImproperCTypesLint{cx}.check_struct_for_power_alignment(item);
1387+
ImproperCTypesLint { cx }.check_struct_for_power_alignment(item);
14301388
}
14311389
// See `check_field_def`..
14321390
hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {}

tests/ui/lint/improper_ctypes/ctypes.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,13 @@ extern "C" {
9494
pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `&str`
9595
pub fn transparent_fn(p: TransparentBoxFn);
9696
pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]`
97-
pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box<TwoBadTypes<'a>>));
98-
//~^ ERROR: uses type `char`
99-
//~^^ ERROR: uses type `&dyn Debug`
97+
pub fn multi_errors_per_arg(
98+
f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box<TwoBadTypes<'a>>)
99+
);
100+
//~^^ ERROR: uses type `char`
101+
//~^^^ ERROR: uses type `&dyn Debug`
100102
// (possible FIXME: the in-struct `char` field doesn't get a warning due ^^)
101-
//~^^^^ ERROR: uses type `&[u8]`
103+
//~^^^^^ ERROR: uses type `&[u8]`
102104

103105
pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign);
104106
pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn`

0 commit comments

Comments
 (0)