@@ -830,6 +830,17 @@ enum TypeSizedness {
830
830
UnsizedWithMetadata ,
831
831
}
832
832
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
+
833
844
/// Is this type unsized because it contains (or is) a foreign type?
834
845
/// (Returns Err if the type happens to be sized after all)
835
846
fn get_type_sizedness < ' tcx , ' a > ( cx : & ' a LateContext < ' tcx > , ty : Ty < ' tcx > ) -> TypeSizedness {
@@ -1192,6 +1203,77 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1192
1203
}
1193
1204
}
1194
1205
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
+
1195
1277
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
1196
1278
/// representation which can be exported to C code).
1197
1279
fn check_type_for_ffi (
@@ -1214,48 +1296,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1214
1296
match * ty. kind ( ) {
1215
1297
ty:: Adt ( def, args) => {
1216
1298
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 ) ;
1259
1300
}
1260
1301
if def. is_phantom_data ( ) {
1261
1302
return FfiPhantom ( ty) ;
@@ -1418,69 +1459,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1418
1459
FfiSafe
1419
1460
}
1420
1461
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 ) ;
1484
1467
}
1485
1468
1486
1469
ty:: Array ( inner_ty, _) => self . check_type_for_ffi ( acc, inner_ty) ,
0 commit comments