Skip to content

Commit efe5695

Browse files
committed
lint ImproperCTypes: smoothing out some nits and un-tidy-ness
1 parent df85b22 commit efe5695

File tree

4 files changed

+86
-119
lines changed

4 files changed

+86
-119
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,13 +422,13 @@ lint_improper_ctypes_ptr_validity_reason =
422422
lint_improper_ctypes_sized_ptr_to_unsafe_type =
423423
this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout
424424
425-
lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field
426425
lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead
427-
428426
lint_improper_ctypes_slice_reason = slices have no C equivalent
429-
lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
430427
428+
lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
431429
lint_improper_ctypes_str_reason = string slices have no C equivalent
430+
431+
lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field
432432
lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct
433433
434434
lint_improper_ctypes_struct_fieldless_reason = this struct has no fields

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 65 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -147,61 +147,28 @@ impl<'tcx> FfiResult<'tcx> {
147147
}
148148

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

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

217-
/// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it
184+
/// Determine if a type is sized or not, and whether it affects references/pointers/boxes to it
218185
#[derive(Clone, Copy)]
219186
enum TypeSizedness {
220187
/// type of definite size (pointers are C-compatible)
@@ -353,51 +320,63 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type
353320
}
354321
}
355322

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

370346
impl CTypesVisitorState {
371-
/// wether the type is used (directly or not) in a static variable
347+
/// whether the type is used (directly or not) in a static variable
372348
fn is_in_static(self) -> bool {
373-
((self as u8) & 0b0010) != 0
349+
use CTypesVisitorStateFlags::*;
350+
((self as u8) & STATIC) != 0
374351
}
375-
/// wether the type is used (directly or not) in a function, in return position
352+
/// whether the type is used (directly or not) in a function, in return position
376353
fn is_in_function_return(self) -> bool {
377-
let ret = ((self as u8) & 0b0100) != 0;
354+
use CTypesVisitorStateFlags::*;
355+
let ret = ((self as u8) & FN_RETURN) != 0;
378356
#[cfg(debug_assertions)]
379357
if ret {
380358
assert!(!self.is_in_static());
381359
}
382360
ret
383361
}
384-
/// wether the type is used (directly or not) in a defined function
385-
/// in other words, wether or not we allow non-FFI-safe types behind a C pointer,
362+
/// whether the type is used (directly or not) in a defined function
363+
/// in other words, whether or not we allow non-FFI-safe types behind a C pointer,
386364
/// to be treated as an opaque type on the other side of the FFI boundary
387365
fn is_in_defined_function(self) -> bool {
388-
let ret = ((self as u8) & 0b1000) != 0;
366+
use CTypesVisitorStateFlags::*;
367+
let ret = ((self as u8) & FN_DECLARED) != 0;
389368
#[cfg(debug_assertions)]
390369
if ret {
391370
assert!(!self.is_in_static());
392371
}
393372
ret
394373
}
395374

396-
/// wether the value for that type might come from the non-rust side of a FFI boundary
375+
/// whether the value for that type might come from the non-rust side of a FFI boundary
397376
fn value_may_be_unchecked(self) -> bool {
398377
// function declarations are assumed to be rust-caller, non-rust-callee
399378
// function definitions are assumed to be maybe-not-rust-caller, rust-callee
400-
// FnPtrs are... well, nothing's certain about anything. (TODO need more flags in enum?)
379+
// FnPtrs are... well, nothing's certain about anything. (FIXME need more flags in enum?)
401380
// Same with statics.
402381
if self.is_in_static() {
403382
true
@@ -410,7 +389,7 @@ impl CTypesVisitorState {
410389
}
411390

412391
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
413-
/// Checks wether an `extern "ABI" fn` function pointer is indeed FFI-safe to call
392+
/// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call
414393
fn visit_fnptr(&mut self, mode: CItemKind, ty: Ty<'tcx>, sig: Sig<'tcx>) -> FfiResult<'tcx> {
415394
use FfiResult::*;
416395
debug_assert!(!sig.abi().is_rustic_abi());
@@ -532,7 +511,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
532511
// there are three remaining concerns with the pointer:
533512
// - is the pointer compatible with a C pointer in the first place? (if not, only send that error message)
534513
// - is the pointee FFI-safe? (it might not matter, see mere lines below)
535-
// - does the pointer type contain a non-zero assumption, but a value given by non-rust code?
514+
// - does the pointer type contain a non-zero assumption, but has a value given by non-rust code?
536515
// this block deals with the first two.
537516
let mut ffi_res = match get_type_sizedness(self.cx, inner_ty) {
538517
TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => {
@@ -581,7 +560,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
581560
// 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions.
582561
// (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't)
583562

584-
// wether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box<impl Trait>),
563+
// whether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box<impl Trait>),
585564
// so let's not wrap the current context around a potential FfiUnsafe type param.
586565
self.visit_type(state, Some(ty), inner_ty)
587566
}
@@ -601,6 +580,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
601580
};
602581

603582
// and now the third concern (does the pointer type contain a non-zero assumption, and is the value given by non-rust code?)
583+
// 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
604584
ffi_res += if state.value_may_be_unchecked() {
605585
let has_nonnull_assumption = match indirection_type {
606586
IndirectionType::RawPtr => false,
@@ -756,18 +736,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
756736

757737
if def.variants().is_empty() {
758738
// Empty enums are implicitely handled as the never type:
759-
// TODO think about the FFI-safety of functions that use that
739+
// FIXME think about the FFI-safety of functions that use that
760740
return FfiSafe;
761741
}
762742
// Check for a repr() attribute to specify the size of the
763743
// discriminant.
764744
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() {
765745
// Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
766-
if let Some(inner_ty) = repr_nullable_ptr(
767-
self.cx.tcx,
768-
self.cx.typing_env(),
769-
ty,
770-
) {
746+
if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) {
771747
return self.visit_type(state, Some(ty), inner_ty);
772748
}
773749

@@ -801,7 +777,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
801777
.iter()
802778
.map(|variant| {
803779
self.visit_variant_fields(state, ty, def, variant, args)
804-
// TODO: check that enums allow any (up to all) variants to be phantoms?
780+
// FIXME: check that enums allow any (up to all) variants to be phantoms?
805781
// (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
806782
.forbid_phantom()
807783
})
@@ -846,11 +822,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
846822
}
847823
match def.adt_kind() {
848824
AdtKind::Struct | AdtKind::Union => {
849-
// I thought CStr (not CString) could not be reached here:
850-
// - not using an indirection would cause a compile error prior to this lint
825+
// I thought CStr (not CString) here could only be reached in non-compiling code:
826+
// - not using an indirection would cause a compile error (this lint *currently* seems to not get triggered on such non-compiling code)
851827
// - and using one would cause the lint to catch on the indirection before reaching its pointee
852-
// but for some reason one can just go and write function *pointers* like that:
853-
// `type Foo = extern "C" fn(::std::ffi::CStr);`
828+
// but function *pointers* don't seem to have the same no-unsized-parameters requirement to compile
854829
if let Some(sym::cstring_type | sym::cstr_type) =
855830
tcx.get_diagnostic_name(def.did())
856831
{
@@ -1097,10 +1072,7 @@ struct ImproperCTypesLint<'c, 'tcx> {
10971072
}
10981073

10991074
impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
1100-
fn check_arg_for_power_alignment(
1101-
&mut self,
1102-
ty: Ty<'tcx>,
1103-
) -> bool {
1075+
fn check_arg_for_power_alignment(&mut self, ty: Ty<'tcx>) -> bool {
11041076
let tcx = self.cx.tcx;
11051077
assert!(tcx.sess.target.os == "aix");
11061078
// Structs (under repr(C)) follow the power alignment rule if:
@@ -1131,10 +1103,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
11311103
return false;
11321104
}
11331105

1134-
fn check_struct_for_power_alignment(
1135-
&mut self,
1136-
item: &'tcx hir::Item<'tcx>,
1137-
) {
1106+
fn check_struct_for_power_alignment(&mut self, item: &'tcx hir::Item<'tcx>) {
11381107
let tcx = self.cx.tcx;
11391108
let adt_def = tcx.adt_def(item.owner_id.to_def_id());
11401109
// repr(C) structs also with packed or aligned representation
@@ -1218,7 +1187,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
12181187
(span, ffi_res)
12191188
})
12201189
// even in function *definitions*, `FnPtr`s are always function declarations ...right?
1221-
// (TODO: we can't do that yet because one of rustc's crates can't compile if we do)
1190+
// (FIXME: we can't do that yet because one of rustc's crates can't compile if we do)
12221191
.for_each(|(span, ffi_res)| self.process_ffi_result(span, ffi_res, fn_mode));
12231192
//.drain();
12241193
}
@@ -1306,12 +1275,8 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
13061275

13071276
// this whole while block converts the arbitrarily-deep
13081277
// FfiResult stack to an ImproperCTypesLayer Vec
1309-
while let ControlFlow::Continue(FfiUnsafeReason {
1310-
ty,
1311-
reason,
1312-
help,
1313-
inner,
1314-
}) = ffiresult_recursor
1278+
while let ControlFlow::Continue(FfiUnsafeReason { ty, reason, help, inner }) =
1279+
ffiresult_recursor
13151280
{
13161281
if let Some(layer) = cimproper_layers.last_mut() {
13171282
layer.inner_ty = Some(ty.clone());
@@ -1374,7 +1339,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
13741339

13751340
match it.kind {
13761341
hir::ForeignItemKind::Fn(sig, _, _) => {
1377-
if abi.is_rustic_abi() {
1342+
if abi.is_rustic_abi() {
13781343
lint.check_fn_for_external_abi_fnptr(
13791344
CItemKind::Declaration,
13801345
it.owner_id.def_id,
@@ -1416,7 +1381,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
14161381
// Structs are checked based on if they follow the power alignment
14171382
// rule (under repr(C)).
14181383
hir::ItemKind::Struct(..) => {
1419-
ImproperCTypesLint{cx}.check_struct_for_power_alignment(item);
1384+
ImproperCTypesLint { cx }.check_struct_for_power_alignment(item);
14201385
}
14211386
// See `check_field_def`..
14221387
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
@@ -91,11 +91,13 @@ extern "C" {
9191
pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `&str`
9292
pub fn transparent_fn(p: TransparentBoxFn);
9393
pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]`
94-
pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box<TwoBadTypes<'a>>));
95-
//~^ ERROR: uses type `char`
96-
//~^^ ERROR: uses type `&dyn Debug`
94+
pub fn multi_errors_per_arg(
95+
f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box<TwoBadTypes<'a>>)
96+
);
97+
//~^^ ERROR: uses type `char`
98+
//~^^^ ERROR: uses type `&dyn Debug`
9799
// (possible FIXME: the in-struct `char` field doesn't get a warning due ^^)
98-
//~^^^^ ERROR: uses type `&[u8]`
100+
//~^^^^^ ERROR: uses type `&[u8]`
99101

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

0 commit comments

Comments
 (0)