Skip to content

Commit 103a8d0

Browse files
committed
ImproperCTypes: remove special cases through better state tracking
Another interal change that shouldn't impact rustc users. Code called outside of `visit_type` (and callees) is moved inside, by adding new types to properly track the state of a type visitation. - OuterTyKind tracks the knowledge of the type "directly outside of" the one being visited (if we are visiting a struct's field, an array's element, etc) - RootUseFlags tracks the knowledge of how the "original type being visited" is used: static variable, function argument/return, etc.
1 parent 6dfa52c commit 103a8d0

1 file changed

Lines changed: 89 additions & 50 deletions

File tree

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 89 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,37 @@ impl VisitorState {
369369
}
370370
}
371371

372+
bitflags! {
373+
/// Data that summarises how an "outer type" surrounds its inner type(s)
374+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
375+
struct OuterTyData: u8 {
376+
/// To show that there is no outer type, the current type is directly used by a `static`
377+
/// variable or a function/FnPtr
378+
const NO_OUTER_TY = 0b01;
379+
/// For NO_OUTER_TY cases, show that we are being directly used by a FnPtr specifically
380+
/// FIXME(ctypes): this is only used for "bad behaviour" reproduced for compatibility's sake
381+
const NO_OUTER_TY_FNPTR = 0b10;
382+
/// Other cases, because currently no other characteristics are used about outer types
383+
const NONE = 0b00;
384+
}
385+
}
386+
387+
impl OuterTyData {
388+
/// Get the proper data for a given outer type.
389+
fn from_ty<'tcx>(ty: Ty<'tcx>) -> Self {
390+
match ty.kind() {
391+
ty::FnPtr(..) => Self::NO_OUTER_TY | Self::NO_OUTER_TY_FNPTR,
392+
ty::RawPtr(..)
393+
| ty::Ref(..)
394+
| ty::Adt(..)
395+
| ty::Tuple(..)
396+
| ty::Array(..)
397+
| ty::Slice(_) => Self::NONE,
398+
k @ _ => bug!("unexpected outer type {:?} of kind {:?}", ty, k),
399+
}
400+
}
401+
}
402+
372403
/// Visitor used to recursively traverse MIR types and evaluate FFI-safety.
373404
/// It uses ``check_*`` methods as entrypoints to be called elsewhere,
374405
/// and ``visit_*`` methods to recurse.
@@ -454,7 +485,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
454485
{
455486
FfiSafe
456487
} else {
457-
self.visit_type(state, inner_ty)
488+
self.visit_type(state, OuterTyData::from_ty(ty), inner_ty)
458489
}
459490
}
460491
}
@@ -475,7 +506,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
475506
if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) {
476507
// Transparent newtypes have at most one non-ZST field which needs to be checked..
477508
let field_ty = get_type_from_field(self.cx, field, args);
478-
match self.visit_type(state, field_ty) {
509+
match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) {
479510
FfiUnsafe { ty, .. } if ty.is_unit() => (),
480511
r => return r,
481512
}
@@ -494,7 +525,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
494525
let mut all_phantom = !variant.fields.is_empty();
495526
for field in &variant.fields {
496527
let field_ty = get_type_from_field(self.cx, field, args);
497-
all_phantom &= match self.visit_type(state, field_ty) {
528+
all_phantom &= match self.visit_type(state, OuterTyData::from_ty(ty), field_ty) {
498529
FfiSafe => false,
499530
// `()` fields are FFI-safe!
500531
FfiUnsafe { ty, .. } if ty.is_unit() => false,
@@ -592,11 +623,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
592623
// Empty enums are okay... although sort of useless.
593624
return FfiSafe;
594625
}
595-
// Check for a repr() attribute to specify the size of the discriminant.
626+
// Check for a repr() attribute to specify the size of the
627+
// discriminant.
596628
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() {
597629
// Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
598-
if let Some(ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) {
599-
return self.visit_type(state, ty);
630+
if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) {
631+
return self.visit_type(state, OuterTyData::from_ty(ty), inner_ty);
600632
}
601633

602634
return FfiUnsafe {
@@ -628,7 +660,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
628660

629661
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
630662
/// representation which can be exported to C code).
631-
fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
663+
fn visit_type(
664+
&mut self,
665+
state: VisitorState,
666+
outer_ty: OuterTyData,
667+
ty: Ty<'tcx>,
668+
) -> FfiResult<'tcx> {
632669
use FfiResult::*;
633670

634671
let tcx = self.cx.tcx;
@@ -672,7 +709,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
672709
// Pattern types are just extra invariants on the type that you need to uphold,
673710
// but only the base type is relevant for being representable in FFI.
674711
// (note: this lint was written when pattern types could only be integers constrained to ranges)
675-
ty::Pat(pat_ty, _) => self.visit_type(state, pat_ty),
712+
ty::Pat(pat_ty, _) => self.visit_type(state, outer_ty, pat_ty),
676713

677714
// types which likely have a stable representation, if the target architecture defines those
678715
// note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64
@@ -702,11 +739,24 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
702739
help: Some(msg!("consider using `*const u8` and a length instead")),
703740
},
704741

705-
ty::Tuple(..) => FfiUnsafe {
706-
ty,
707-
reason: msg!("tuples have unspecified layout"),
708-
help: Some(msg!("consider using a struct instead")),
709-
},
742+
ty::Tuple(tuple) => {
743+
let empty_and_safe = if tuple.is_empty() {
744+
// C functions can return void
745+
outer_ty.contains(OuterTyData::NO_OUTER_TY) && state.is_in_function_return()
746+
} else {
747+
false
748+
};
749+
750+
if empty_and_safe {
751+
FfiSafe
752+
} else {
753+
FfiUnsafe {
754+
ty,
755+
reason: msg!("tuples have unspecified layout"),
756+
help: Some(msg!("consider using a struct instead")),
757+
}
758+
}
759+
}
710760

711761
ty::RawPtr(ty, _)
712762
if match ty.kind() {
@@ -724,7 +774,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
724774
return self.visit_indirection(state, ty, inner_ty, IndirectionKind::Ref);
725775
}
726776

727-
ty::Array(inner_ty, _) => self.visit_type(state, inner_ty),
777+
ty::Array(inner_ty, _) => {
778+
if state.is_in_function()
779+
&& outer_ty.contains(OuterTyData::NO_OUTER_TY)
780+
// FIXME(ctypes): VVV-this-VVV shouldn't be the case
781+
&& !outer_ty.contains(OuterTyData::NO_OUTER_TY_FNPTR)
782+
{
783+
// C doesn't really support passing arrays by value - the only way to pass an array by value
784+
// is through a struct.
785+
FfiResult::FfiUnsafe {
786+
ty,
787+
reason: msg!("passing raw arrays by value is not FFI-safe"),
788+
help: Some(msg!("consider passing a pointer to the array")),
789+
}
790+
} else {
791+
// let's allow phantoms to go through,
792+
// since an array of 1-ZSTs is also a 1-ZST
793+
self.visit_type(state, OuterTyData::from_ty(ty), inner_ty)
794+
}
795+
}
728796

729797
ty::FnPtr(sig_tys, hdr) => {
730798
let sig = sig_tys.with(hdr);
@@ -740,18 +808,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
740808

741809
let sig = tcx.instantiate_bound_regions_with_erased(sig);
742810
for arg in sig.inputs() {
743-
match self.visit_type(VisitorState::ARGUMENT_TY_IN_FNPTR, *arg) {
811+
match self.visit_type(
812+
VisitorState::ARGUMENT_TY_IN_FNPTR,
813+
OuterTyData::from_ty(ty),
814+
*arg,
815+
) {
744816
FfiSafe => {}
745817
r => return r,
746818
}
747819
}
748820

749821
let ret_ty = sig.output();
750-
if ret_ty.is_unit() {
751-
return FfiSafe;
752-
}
753822

754-
self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, ret_ty)
823+
self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, OuterTyData::from_ty(ty), ret_ty)
755824
}
756825

757826
ty::Foreign(..) => FfiSafe,
@@ -819,43 +888,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
819888
})
820889
}
821890

822-
/// Check if the type is array and emit an unsafe type lint.
823-
fn check_for_array_ty(&mut self, ty: Ty<'tcx>) -> PartialFfiResult<'tcx> {
824-
if let ty::Array(..) = ty.kind() {
825-
Some(FfiResult::FfiUnsafe {
826-
ty,
827-
reason: msg!("passing raw arrays by value is not FFI-safe"),
828-
help: Some(msg!("consider passing a pointer to the array")),
829-
})
830-
} else {
831-
None
832-
}
833-
}
834-
835-
/// Determine the FFI-safety of a single (MIR) type, given the context of how it is used.
836891
fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
837892
let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
838893
if let Some(res) = self.visit_for_opaque_ty(ty) {
839894
return res;
840895
}
841896

842-
// C doesn't really support passing arrays by value - the only way to pass an array by value
843-
// is through a struct. So, first test that the top level isn't an array, and then
844-
// recursively check the types inside.
845-
if state.is_in_function() {
846-
if let Some(res) = self.check_for_array_ty(ty) {
847-
return res;
848-
}
849-
}
850-
851-
// Don't report FFI errors for unit return types. This check exists here, and not in
852-
// the caller (where it would make more sense) so that normalization has definitely
853-
// happened.
854-
if state.is_in_function_return() && ty.is_unit() {
855-
return FfiResult::FfiSafe;
856-
}
857-
858-
self.visit_type(state, ty)
897+
self.visit_type(state, OuterTyData::NO_OUTER_TY, ty)
859898
}
860899
}
861900

0 commit comments

Comments
 (0)