@@ -830,6 +830,17 @@ enum TypeSizedness {
830830 UnsizedWithMetadata ,
831831}
832832
833+ /// what type indirection points to a given type
834+ #[ derive( Clone , Copy ) ]
835+ enum IndirectionType {
836+ /// box (valid non-null pointer, owns pointee)
837+ Box ,
838+ /// ref (valid non-null pointer, borrows pointee)
839+ Ref ,
840+ /// raw pointer (not necessarily non-null or valid. no info on ownership)
841+ RawPtr ,
842+ }
843+
833844/// Is this type unsized because it contains (or is) a foreign type?
834845/// (Returns Err if the type happens to be sized after all)
835846fn get_type_sizedness < ' tcx , ' a > ( cx : & ' a LateContext < ' tcx > , ty : Ty < ' tcx > ) -> TypeSizedness {
@@ -1192,6 +1203,77 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11921203 }
11931204 }
11941205
1206+ /// Checks if the given indirection (box,ref,pointer) is "ffi-safe"
1207+ fn check_indirection_for_ffi (
1208+ & self ,
1209+ acc : & mut CTypesVisitorState < ' tcx > ,
1210+ ty : Ty < ' tcx > ,
1211+ inner_ty : Ty < ' tcx > ,
1212+ indirection_type : IndirectionType ,
1213+ ) -> FfiResult < ' tcx > {
1214+ use FfiResult :: * ;
1215+ let tcx = self . cx . tcx ;
1216+ match get_type_sizedness ( self . cx , inner_ty) {
1217+ TypeSizedness :: UnsizedWithExternType | TypeSizedness :: Definite => {
1218+ // there's a nuance on what this lint should do for
1219+ // function definitions (`extern "C" fn fn_name(...) {...}`)
1220+ // versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
1221+ // This is touched upon in https://github.com/rust-lang/rust/issues/66220
1222+ // and https://github.com/rust-lang/rust/pull/72700
1223+ //
1224+ // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1225+ // (which has a stable layout) but points to FFI-unsafe type, is it safe?
1226+ // On one hand, the function's ABI will match that of a similar C-declared function API,
1227+ // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
1228+ // In this code, the opinion on is split between function declarations and function definitions,
1229+ // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
1230+ // For declarations, we see this as unsafe, but for definitions, we see this as safe.
1231+ //
1232+ // For extern function declarations, the actual definition of the function is written somewhere else,
1233+ // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side)
1234+ // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
1235+ // and having the full type information is necessary to compile the function.
1236+ if matches ! ( self . mode, CItemKind :: Definition ) {
1237+ return FfiSafe ;
1238+ } else {
1239+ let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
1240+ return match inner_res {
1241+ FfiSafe => inner_res,
1242+ _ => FfiUnsafeWrapper {
1243+ ty,
1244+ reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
1245+ wrapped : Box :: new ( inner_res) ,
1246+ help : None ,
1247+ } ,
1248+ } ;
1249+ }
1250+ }
1251+ TypeSizedness :: UnsizedWithMetadata => {
1252+ let help = match inner_ty. kind ( ) {
1253+ ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
1254+ ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
1255+ ty:: Adt ( def, _)
1256+ if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
1257+ && matches ! (
1258+ tcx. get_diagnostic_name( def. did( ) ) ,
1259+ Some ( sym:: cstring_type | sym:: cstr_type)
1260+ )
1261+ && !acc. base_ty . is_mutable_ptr ( ) =>
1262+ {
1263+ Some ( fluent:: lint_improper_ctypes_cstr_help)
1264+ }
1265+ _ => None ,
1266+ } ;
1267+ let reason = match indirection_type {
1268+ IndirectionType :: RawPtr => fluent:: lint_improper_ctypes_unsized_ptr,
1269+ IndirectionType :: Ref => fluent:: lint_improper_ctypes_unsized_ref,
1270+ IndirectionType :: Box => fluent:: lint_improper_ctypes_unsized_box,
1271+ } ;
1272+ FfiUnsafe { ty, reason, help }
1273+ }
1274+ }
1275+ }
1276+
11951277 /// Checks if the given type is "ffi-safe" (has a stable, well-defined
11961278 /// representation which can be exported to C code).
11971279 fn check_type_for_ffi (
@@ -1214,48 +1296,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12141296 match * ty. kind ( ) {
12151297 ty:: Adt ( def, args) => {
12161298 if let Some ( inner_ty) = ty. boxed_ty ( ) {
1217- if let TypeSizedness :: UnsizedWithExternType | TypeSizedness :: Definite =
1218- get_type_sizedness ( self . cx , inner_ty)
1219- {
1220- // discussion on declaration vs definition:
1221- // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
1222- // of this `match *ty.kind()` block
1223- if matches ! ( self . mode, CItemKind :: Definition ) {
1224- return FfiSafe ;
1225- } else {
1226- let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
1227- return match inner_res {
1228- FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper {
1229- ty,
1230- reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
1231- wrapped : Box :: new ( inner_res) ,
1232- help : None ,
1233- } ,
1234- _ => inner_res,
1235- } ;
1236- }
1237- } else {
1238- let help = match inner_ty. kind ( ) {
1239- ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
1240- ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
1241- ty:: Adt ( def, _)
1242- if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
1243- && matches ! (
1244- tcx. get_diagnostic_name( def. did( ) ) ,
1245- Some ( sym:: cstring_type | sym:: cstr_type)
1246- )
1247- && !acc. base_ty . is_mutable_ptr ( ) =>
1248- {
1249- Some ( fluent:: lint_improper_ctypes_cstr_help)
1250- }
1251- _ => None ,
1252- } ;
1253- return FfiUnsafe {
1254- ty,
1255- reason : fluent:: lint_improper_ctypes_unsized_box,
1256- help,
1257- } ;
1258- }
1299+ return self . check_indirection_for_ffi ( acc, ty, inner_ty, IndirectionType :: Box ) ;
12591300 }
12601301 if def. is_phantom_data ( ) {
12611302 return FfiPhantom ( ty) ;
@@ -1418,69 +1459,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
14181459 FfiSafe
14191460 }
14201461
1421- ty:: RawPtr ( inner_ty, _) | ty:: Ref ( _, inner_ty, _) => {
1422- if let TypeSizedness :: UnsizedWithExternType | TypeSizedness :: Definite =
1423- get_type_sizedness ( self . cx , inner_ty)
1424- {
1425- // there's a nuance on what this lint should do for
1426- // function definitions (`extern "C" fn fn_name(...) {...}`)
1427- // versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
1428- // This is touched upon in https://github.com/rust-lang/rust/issues/66220
1429- // and https://github.com/rust-lang/rust/pull/72700
1430- //
1431- // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1432- // (which has a stable layout) but points to FFI-unsafe type, is it safe?
1433- // On one hand, the function's ABI will match that of a similar C-declared function API,
1434- // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
1435- // In this code, the opinion on is split between function declarations and function definitions,
1436- // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
1437- // For declarations, we see this as unsafe, but for definitions, we see this as safe.
1438- //
1439- // For extern function declarations, the actual definition of the function is written somewhere else,
1440- // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side)
1441- // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
1442- // and having the full type information is necessary to compile the function.
1443- if matches ! ( self . mode, CItemKind :: Definition ) {
1444- return FfiSafe ;
1445- } else if matches ! ( ty. kind( ) , ty:: RawPtr ( ..) )
1446- && matches ! ( inner_ty. kind( ) , ty:: Tuple ( tuple) if tuple. is_empty( ) )
1447- {
1448- FfiSafe
1449- } else {
1450- let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
1451- return match inner_res {
1452- FfiSafe => inner_res,
1453- _ => FfiUnsafeWrapper {
1454- ty,
1455- reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
1456- wrapped : Box :: new ( inner_res) ,
1457- help : None ,
1458- } ,
1459- } ;
1460- }
1461- } else {
1462- let help = match inner_ty. kind ( ) {
1463- ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
1464- ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
1465- ty:: Adt ( def, _)
1466- if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
1467- && matches ! (
1468- tcx. get_diagnostic_name( def. did( ) ) ,
1469- Some ( sym:: cstring_type | sym:: cstr_type)
1470- )
1471- && !acc. base_ty . is_mutable_ptr ( ) =>
1472- {
1473- Some ( fluent:: lint_improper_ctypes_cstr_help)
1474- }
1475- _ => None ,
1476- } ;
1477- let reason = match ty. kind ( ) {
1478- ty:: RawPtr ( ..) => fluent:: lint_improper_ctypes_unsized_ptr,
1479- ty:: Ref ( ..) => fluent:: lint_improper_ctypes_unsized_ref,
1480- _ => unreachable ! ( ) ,
1481- } ;
1482- FfiUnsafe { ty, reason, help }
1483- }
1462+ ty:: RawPtr ( inner_ty, _) => {
1463+ return self . check_indirection_for_ffi ( acc, ty, inner_ty, IndirectionType :: RawPtr ) ;
1464+ }
1465+ ty:: Ref ( _, inner_ty, _) => {
1466+ return self . check_indirection_for_ffi ( acc, ty, inner_ty, IndirectionType :: Ref ) ;
14841467 }
14851468
14861469 ty:: Array ( inner_ty, _) => self . check_type_for_ffi ( acc, inner_ty) ,
0 commit comments