@@ -849,6 +849,17 @@ enum TypeSizedness {
849
849
UnsizedWithMetadata ,
850
850
}
851
851
852
+ /// what type indirection points to a given type
853
+ #[ derive( Clone , Copy ) ]
854
+ enum IndirectionType {
855
+ /// box (valid non-null pointer, owns pointee)
856
+ Box ,
857
+ /// ref (valid non-null pointer, borrows pointee)
858
+ Ref ,
859
+ /// raw pointer (not necessarily non-null or valid. no info on ownership)
860
+ RawPtr ,
861
+ }
862
+
852
863
/// Is this type unsized because it contains (or is) a foreign type?
853
864
/// (Returns Err if the type happens to be sized after all)
854
865
fn get_type_sizedness < ' tcx , ' a > ( cx : & ' a LateContext < ' tcx > , ty : Ty < ' tcx > ) -> TypeSizedness {
@@ -1261,6 +1272,77 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1261
1272
}
1262
1273
}
1263
1274
1275
+ /// Checks if the given indirection (box,ref,pointer) is "ffi-safe"
1276
+ fn check_indirection_for_ffi (
1277
+ & self ,
1278
+ acc : & mut CTypesVisitorState < ' tcx > ,
1279
+ ty : Ty < ' tcx > ,
1280
+ inner_ty : Ty < ' tcx > ,
1281
+ indirection_type : IndirectionType ,
1282
+ ) -> FfiResult < ' tcx > {
1283
+ use FfiResult :: * ;
1284
+ let tcx = self . cx . tcx ;
1285
+ match get_type_sizedness ( self . cx , inner_ty) {
1286
+ TypeSizedness :: UnsizedWithExternType | TypeSizedness :: Definite => {
1287
+ // there's a nuance on what this lint should do for
1288
+ // function definitions (`extern "C" fn fn_name(...) {...}`)
1289
+ // versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
1290
+ // This is touched upon in https://github.com/rust-lang/rust/issues/66220
1291
+ // and https://github.com/rust-lang/rust/pull/72700
1292
+ //
1293
+ // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1294
+ // (which has a stable layout) but points to FFI-unsafe type, is it safe?
1295
+ // On one hand, the function's ABI will match that of a similar C-declared function API,
1296
+ // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
1297
+ // In this code, the opinion on is split between function declarations and function definitions,
1298
+ // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
1299
+ // For declarations, we see this as unsafe, but for definitions, we see this as safe.
1300
+ //
1301
+ // For extern function declarations, the actual definition of the function is written somewhere else,
1302
+ // 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)
1303
+ // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
1304
+ // and having the full type information is necessary to compile the function.
1305
+ if matches ! ( self . mode, CItemKind :: Definition ) {
1306
+ return FfiSafe ;
1307
+ } else {
1308
+ let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
1309
+ return match inner_res {
1310
+ FfiSafe => inner_res,
1311
+ _ => FfiUnsafeWrapper {
1312
+ ty,
1313
+ reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
1314
+ wrapped : Box :: new ( inner_res) ,
1315
+ help : None ,
1316
+ } ,
1317
+ } ;
1318
+ }
1319
+ }
1320
+ TypeSizedness :: UnsizedWithMetadata => {
1321
+ let help = match inner_ty. kind ( ) {
1322
+ ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
1323
+ ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
1324
+ ty:: Adt ( def, _)
1325
+ if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
1326
+ && matches ! (
1327
+ tcx. get_diagnostic_name( def. did( ) ) ,
1328
+ Some ( sym:: cstring_type | sym:: cstr_type)
1329
+ )
1330
+ && !acc. base_ty . is_mutable_ptr ( ) =>
1331
+ {
1332
+ Some ( fluent:: lint_improper_ctypes_cstr_help)
1333
+ }
1334
+ _ => None ,
1335
+ } ;
1336
+ let reason = match indirection_type {
1337
+ IndirectionType :: RawPtr => fluent:: lint_improper_ctypes_unsized_ptr,
1338
+ IndirectionType :: Ref => fluent:: lint_improper_ctypes_unsized_ref,
1339
+ IndirectionType :: Box => fluent:: lint_improper_ctypes_unsized_box,
1340
+ } ;
1341
+ FfiUnsafe { ty, reason, help }
1342
+ }
1343
+ }
1344
+ }
1345
+
1264
1346
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
1265
1347
/// representation which can be exported to C code).
1266
1348
fn check_type_for_ffi (
@@ -1283,48 +1365,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1283
1365
match * ty. kind ( ) {
1284
1366
ty:: Adt ( def, args) => {
1285
1367
if let Some ( inner_ty) = ty. boxed_ty ( ) {
1286
- if let TypeSizedness :: UnsizedWithExternType | TypeSizedness :: Definite =
1287
- get_type_sizedness ( self . cx , inner_ty)
1288
- {
1289
- // discussion on declaration vs definition:
1290
- // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
1291
- // of this `match *ty.kind()` block
1292
- if matches ! ( self . mode, CItemKind :: Definition ) {
1293
- return FfiSafe ;
1294
- } else {
1295
- let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
1296
- return match inner_res {
1297
- FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper {
1298
- ty,
1299
- reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
1300
- wrapped : Box :: new ( inner_res) ,
1301
- help : None ,
1302
- } ,
1303
- _ => inner_res,
1304
- } ;
1305
- }
1306
- } else {
1307
- let help = match inner_ty. kind ( ) {
1308
- ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
1309
- ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
1310
- ty:: Adt ( def, _)
1311
- if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
1312
- && matches ! (
1313
- tcx. get_diagnostic_name( def. did( ) ) ,
1314
- Some ( sym:: cstring_type | sym:: cstr_type)
1315
- )
1316
- && !acc. base_ty . is_mutable_ptr ( ) =>
1317
- {
1318
- Some ( fluent:: lint_improper_ctypes_cstr_help)
1319
- }
1320
- _ => None ,
1321
- } ;
1322
- return FfiUnsafe {
1323
- ty,
1324
- reason : fluent:: lint_improper_ctypes_unsized_box,
1325
- help,
1326
- } ;
1327
- }
1368
+ return self . check_indirection_for_ffi ( acc, ty, inner_ty, IndirectionType :: Box ) ;
1328
1369
}
1329
1370
if def. is_phantom_data ( ) {
1330
1371
return FfiPhantom ( ty) ;
@@ -1477,69 +1518,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1477
1518
FfiSafe
1478
1519
}
1479
1520
1480
- ty:: RawPtr ( inner_ty, _) | ty:: Ref ( _, inner_ty, _) => {
1481
- if let TypeSizedness :: UnsizedWithExternType | TypeSizedness :: Definite =
1482
- get_type_sizedness ( self . cx , inner_ty)
1483
- {
1484
- // there's a nuance on what this lint should do for
1485
- // function definitions (`extern "C" fn fn_name(...) {...}`)
1486
- // versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
1487
- // This is touched upon in https://github.com/rust-lang/rust/issues/66220
1488
- // and https://github.com/rust-lang/rust/pull/72700
1489
- //
1490
- // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1491
- // (which has a stable layout) but points to FFI-unsafe type, is it safe?
1492
- // On one hand, the function's ABI will match that of a similar C-declared function API,
1493
- // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
1494
- // In this code, the opinion on is split between function declarations and function definitions,
1495
- // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
1496
- // For declarations, we see this as unsafe, but for definitions, we see this as safe.
1497
- //
1498
- // For extern function declarations, the actual definition of the function is written somewhere else,
1499
- // 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)
1500
- // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
1501
- // and having the full type information is necessary to compile the function.
1502
- if matches ! ( self . mode, CItemKind :: Definition ) {
1503
- return FfiSafe ;
1504
- } else if matches ! ( ty. kind( ) , ty:: RawPtr ( ..) )
1505
- && matches ! ( inner_ty. kind( ) , ty:: Tuple ( tuple) if tuple. is_empty( ) )
1506
- {
1507
- FfiSafe
1508
- } else {
1509
- let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
1510
- return match inner_res {
1511
- FfiSafe => inner_res,
1512
- _ => FfiUnsafeWrapper {
1513
- ty,
1514
- reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
1515
- wrapped : Box :: new ( inner_res) ,
1516
- help : None ,
1517
- } ,
1518
- } ;
1519
- }
1520
- } else {
1521
- let help = match inner_ty. kind ( ) {
1522
- ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
1523
- ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
1524
- ty:: Adt ( def, _)
1525
- if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
1526
- && matches ! (
1527
- tcx. get_diagnostic_name( def. did( ) ) ,
1528
- Some ( sym:: cstring_type | sym:: cstr_type)
1529
- )
1530
- && !acc. base_ty . is_mutable_ptr ( ) =>
1531
- {
1532
- Some ( fluent:: lint_improper_ctypes_cstr_help)
1533
- }
1534
- _ => None ,
1535
- } ;
1536
- let reason = match ty. kind ( ) {
1537
- ty:: RawPtr ( ..) => fluent:: lint_improper_ctypes_unsized_ptr,
1538
- ty:: Ref ( ..) => fluent:: lint_improper_ctypes_unsized_ref,
1539
- _ => unreachable ! ( ) ,
1540
- } ;
1541
- FfiUnsafe { ty, reason, help }
1542
- }
1521
+ ty:: RawPtr ( inner_ty, _) => {
1522
+ return self . check_indirection_for_ffi ( acc, ty, inner_ty, IndirectionType :: RawPtr ) ;
1523
+ }
1524
+ ty:: Ref ( _, inner_ty, _) => {
1525
+ return self . check_indirection_for_ffi ( acc, ty, inner_ty, IndirectionType :: Ref ) ;
1543
1526
}
1544
1527
1545
1528
ty:: Array ( inner_ty, _) => self . check_type_for_ffi ( acc, inner_ty) ,
0 commit comments