Skip to content

Commit b501850

Browse files
committed
lint ImproperCTypes: smoothing out some nits and un-tidy-ness
1 parent 5036d7f commit b501850

File tree

5 files changed

+79
-109
lines changed

5 files changed

+79
-109
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,13 +411,13 @@ lint_improper_ctypes_ptr_validity_reason =
411411
lint_improper_ctypes_sized_ptr_to_unsafe_type =
412412
this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout
413413
414-
lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field
415414
lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead
416-
417415
lint_improper_ctypes_slice_reason = slices have no C equivalent
418-
lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
419416
417+
lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
420418
lint_improper_ctypes_str_reason = string slices have no C equivalent
419+
420+
lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field
421421
lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct
422422
423423
lint_improper_ctypes_struct_fieldless_reason = this struct has no fields

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 54 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
2525

2626
type Sig<'tcx> = Binder<TyCtxt<'tcx>, FnSig<TyCtxt<'tcx>>>;
2727

28-
/// for a given `extern "ABI"`, tell wether that ABI is *not* considered a FFI boundary
28+
/// for a given `extern "ABI"`, tell whether that ABI is *not* considered a FFI boundary
2929
fn fn_abi_is_internal(abi: ExternAbi) -> bool {
3030
matches!(
3131
abi,
@@ -168,57 +168,21 @@ impl<'tcx> std::ops::AddAssign<FfiResult<'tcx>> for FfiResult<'tcx> {
168168
// note: we shouldn't really encounter FfiPhantoms here, they should be dealt with beforehand
169169
// still, this function deals with them in a reasonable way, I think
170170

171-
// this function is awful to look but that's because matching mutable references consumes them (?!)
172-
// the function itself imitates the following piece of non-compiling code:
173-
174-
// match (self, other) {
175-
// (Self::FfiUnsafe(_), _) => {
176-
// // nothing to do
177-
// },
178-
// (_, Self::FfiUnsafe(_)) => {
179-
// *self = other;
180-
// },
181-
// (Self::FfiPhantom(ref ty1),Self::FfiPhantom(ty2)) => {
182-
// println!("whoops, both FfiPhantom: self({:?}) += other({:?})", ty1, ty2);
183-
// },
184-
// (Self::FfiSafe,Self::FfiPhantom(_)) => {
185-
// *self = other;
186-
// },
187-
// (_, Self::FfiSafe) => {
188-
// // nothing to do
189-
// },
190-
// }
191-
192-
let s_disc = std::mem::discriminant(self);
193-
let o_disc = std::mem::discriminant(&other);
194-
if s_disc == o_disc {
195-
match (self, &mut other) {
196-
(Self::FfiUnsafe(ref mut s_inner), Self::FfiUnsafe(ref mut o_inner)) => {
197-
s_inner.append(o_inner);
198-
}
199-
(Self::FfiPhantom(ref ty1), Self::FfiPhantom(ty2)) => {
200-
debug!("whoops: both FfiPhantom, self({:?}) += other({:?})", ty1, ty2);
201-
}
202-
(Self::FfiSafe, Self::FfiSafe) => {}
203-
_ => unreachable!(),
171+
match (self, other) {
172+
(Self::FfiUnsafe(_), _) => {
173+
// nothing to do
204174
}
205-
} else {
206-
if let Self::FfiUnsafe(_) = self {
207-
return;
175+
(myself, other @ Self::FfiUnsafe(_)) => {
176+
*myself = other;
208177
}
209-
match other {
210-
Self::FfiUnsafe(o_inner) => {
211-
// self is Safe or Phantom: Unsafe wins
212-
*self = Self::FfiUnsafe(o_inner);
213-
}
214-
Self::FfiSafe => {
215-
// self is always "wins"
216-
return;
217-
}
218-
Self::FfiPhantom(o_inner) => {
219-
// self is Safe: Phantom wins
220-
*self = Self::FfiPhantom(o_inner);
221-
}
178+
(Self::FfiPhantom(ref ty1), Self::FfiPhantom(ty2)) => {
179+
debug!("whoops, both FfiPhantom: self({:?}) += other({:?})", ty1, ty2);
180+
}
181+
(myself @ Self::FfiSafe, other @ Self::FfiPhantom(_)) => {
182+
*myself = other;
183+
}
184+
(_, Self::FfiSafe) => {
185+
// nothing to do
222186
}
223187
}
224188
}
@@ -231,7 +195,7 @@ impl<'tcx> std::ops::Add<FfiResult<'tcx>> for FfiResult<'tcx> {
231195
}
232196
}
233197

234-
/// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it
198+
/// Determine if a type is sized or not, and whether it affects references/pointers/boxes to it
235199
#[derive(Clone, Copy)]
236200
enum TypeSizedness {
237201
/// type of definite size (pointers are C-compatible)
@@ -370,51 +334,59 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
370334
}
371335
}
372336

337+
mod CTypesVisitorStateFlags{
338+
pub(super) const NO_FLAGS: isize = 0b0000;
339+
/// for static variables (not used in functions)
340+
pub(super) const STATIC: isize = 0b0010;
341+
/// for variables in function returns (implicitly: not for static variables)
342+
pub(super) const FN_RETURN: isize = 0b0100;
343+
/// for variables in functions which are defined in rust (implicitly: not for static variables)
344+
pub(super) const FN_DECLARED: isize = 0b1000;
345+
}
346+
373347
#[repr(u8)]
374348
#[derive(Clone, Copy, Debug)]
375349
enum CTypesVisitorState {
376-
// bitflags:
377-
// 0010: static
378-
// 0100: function return
379-
// 1000: used in declared function
380-
StaticTy = 0b0010,
381-
ArgumentTyInDefinition = 0b1000,
382-
ReturnTyInDefinition = 0b1100,
383-
ArgumentTyInDeclaration = 0b0000,
384-
ReturnTyInDeclaration = 0b0100,
350+
// uses bitflags from CTypesVisitorStateFlags
351+
StaticTy = CTypesVisitorStateFlags::STATIC,
352+
ArgumentTyInDefinition = CTypesVisitorStateFlags::FN_DECLARED,
353+
ReturnTyInDefinition = CTypesVisitorStateFlags::FN_DECLARED|CTypesVisitorStateFlags::FN_RETURN,
354+
ArgumentTyInDeclaration = CTypesVisitorStateFlags::NO_FLAGS,
355+
ReturnTyInDeclaration = CTypesVisitorStateFlags::FN_RETURN,
385356
}
386357

387358
impl CTypesVisitorState {
388-
/// wether the type is used (directly or not) in a static variable
359+
use CTypesVisitorStateFlags::*;
360+
/// whether the type is used (directly or not) in a static variable
389361
fn is_in_static(self) -> bool {
390-
((self as u8) & 0b0010) != 0
362+
((self as u8) & STATIC) != 0
391363
}
392-
/// wether the type is used (directly or not) in a function, in return position
364+
/// whether the type is used (directly or not) in a function, in return position
393365
fn is_in_function_return(self) -> bool {
394-
let ret = ((self as u8) & 0b0100) != 0;
366+
let ret = ((self as u8) & FN_RETURN) != 0;
395367
#[cfg(debug_assertions)]
396368
if ret {
397369
assert!(!self.is_in_static());
398370
}
399371
ret
400372
}
401-
/// wether the type is used (directly or not) in a defined function
402-
/// in other words, wether or not we allow non-FFI-safe types behind a C pointer,
373+
/// whether the type is used (directly or not) in a defined function
374+
/// in other words, whether or not we allow non-FFI-safe types behind a C pointer,
403375
/// to be treated as an opaque type on the other side of the FFI boundary
404376
fn is_in_defined_function(self) -> bool {
405-
let ret = ((self as u8) & 0b1000) != 0;
377+
let ret = ((self as u8) & FN_DECLARED) != 0;
406378
#[cfg(debug_assertions)]
407379
if ret {
408380
assert!(!self.is_in_static());
409381
}
410382
ret
411383
}
412384

413-
/// wether the value for that type might come from the non-rust side of a FFI boundary
385+
/// whether the value for that type might come from the non-rust side of a FFI boundary
414386
fn value_may_be_unchecked(self) -> bool {
415387
// function declarations are assumed to be rust-caller, non-rust-callee
416388
// function definitions are assumed to be maybe-not-rust-caller, rust-callee
417-
// FnPtrs are... well, nothing's certain about anything. (TODO need more flags in enum?)
389+
// FnPtrs are... well, nothing's certain about anything. (FIXME need more flags in enum?)
418390
// Same with statics.
419391
if self.is_in_static() {
420392
true
@@ -427,7 +399,7 @@ impl CTypesVisitorState {
427399
}
428400

429401
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
430-
/// Checks wether an `extern "ABI" fn` function pointer is indeed FFI-safe to call
402+
/// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call
431403
fn visit_fnptr(&mut self, mode: CItemKind, ty: Ty<'tcx>, sig: Sig<'tcx>) -> FfiResult<'tcx> {
432404
use FfiResult::*;
433405
debug_assert!(!fn_abi_is_internal(sig.abi()));
@@ -550,7 +522,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
550522
// there are three remaining concerns with the pointer:
551523
// - is the pointer compatible with a C pointer in the first place? (if not, only send that error message)
552524
// - is the pointee FFI-safe? (it might not matter, see mere lines below)
553-
// - does the pointer type contain a non-zero assumption, but a value given by non-rust code?
525+
// - does the pointer type contain a non-zero assumption, but has a value given by non-rust code?
554526
// this block deals with the first two.
555527
let mut ffi_res = match get_type_sizedness(self.cx, inner_ty) {
556528
TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => {
@@ -599,7 +571,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
599571
// 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions.
600572
// (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't)
601573

602-
// wether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box<impl Trait>),
574+
// whether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box<impl Trait>),
603575
// so let's not wrap the current context around a potential FfiUnsafe type param.
604576
self.visit_type(state, Some(ty), inner_ty)
605577
}
@@ -619,6 +591,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
619591
};
620592

621593
// and now the third concern (does the pointer type contain a non-zero assumption, and is the value given by non-rust code?)
594+
// 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
622595
ffi_res += if state.value_may_be_unchecked() {
623596
let has_nonnull_assumption = match indirection_type {
624597
IndirectionType::RawPtr => false,
@@ -775,7 +748,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
775748

776749
if def.variants().is_empty() {
777750
// Empty enums are implicitely handled as the never type:
778-
// TODO think about the FFI-safety of functions that use that
751+
// FIXME think about the FFI-safety of functions that use that
779752
return FfiSafe;
780753
}
781754
// Check for a repr() attribute to specify the size of the
@@ -820,7 +793,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
820793
.iter()
821794
.map(|variant| {
822795
self.visit_variant_fields(state, ty, def, variant, args)
823-
// TODO: check that enums allow any (up to all) variants to be phantoms?
796+
// FIXME: check that enums allow any (up to all) variants to be phantoms?
824797
// (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
825798
.forbid_phantom()
826799
})
@@ -865,11 +838,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
865838
}
866839
match def.adt_kind() {
867840
AdtKind::Struct | AdtKind::Union => {
868-
// I thought CStr (not CString) could not be reached here:
869-
// - not using an indirection would cause a compile error prior to this lint
841+
// I thought CStr (not CString) here could only be reached in non-compiling code:
842+
// - not using an indirection would cause a compile error (this lint *currently* seems to not get triggered on such non-compiling code)
870843
// - and using one would cause the lint to catch on the indirection before reaching its pointee
871-
// but for some reason one can just go and write function *pointers* like that:
872-
// `type Foo = extern "C" fn(::std::ffi::CStr);`
844+
// but function *pointers* don't seem to have the same no-unsized-parameters requirement to compile
873845
if let Some(sym::cstring_type | sym::cstr_type) =
874846
tcx.get_diagnostic_name(def.did())
875847
{
@@ -1116,10 +1088,7 @@ struct ImproperCTypesLint<'c, 'tcx> {
11161088
}
11171089

11181090
impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
1119-
fn check_arg_for_power_alignment(
1120-
&mut self,
1121-
ty: Ty<'tcx>,
1122-
) -> bool {
1091+
fn check_arg_for_power_alignment(&mut self, ty: Ty<'tcx>) -> bool {
11231092
// Structs (under repr(C)) follow the power alignment rule if:
11241093
// - the first field of the struct is a floating-point type that
11251094
// is greater than 4-bytes, or
@@ -1149,10 +1118,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
11491118
return false;
11501119
}
11511120

1152-
fn check_struct_for_power_alignment(
1153-
&mut self,
1154-
item: &'tcx hir::Item<'tcx>,
1155-
) {
1121+
fn check_struct_for_power_alignment(&mut self, item: &'tcx hir::Item<'tcx>) {
11561122
let tcx = self.cx.tcx;
11571123
let adt_def = tcx.adt_def(item.owner_id.to_def_id());
11581124
if adt_def.repr().c()
@@ -1236,7 +1202,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
12361202
(span, ffi_res)
12371203
})
12381204
// even in function *definitions*, `FnPtr`s are always function declarations ...right?
1239-
// (TODO: we can't do that yet because one of rustc's crates can't compile if we do)
1205+
// (FIXME: we can't do that yet because one of rustc's crates can't compile if we do)
12401206
.for_each(|(span, ffi_res)| self.process_ffi_result(span, ffi_res, fn_mode));
12411207
//.drain();
12421208
}
@@ -1434,7 +1400,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
14341400
// Structs are checked based on if they follow the power alignment
14351401
// rule (under repr(C)).
14361402
hir::ItemKind::Struct(..) => {
1437-
ImproperCTypesLint{cx}.check_struct_for_power_alignment(item);
1403+
ImproperCTypesLint { cx }.check_struct_for_power_alignment(item);
14381404
}
14391405
// See `check_field_def`..
14401406
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)