@@ -14,7 +14,7 @@ use rustc_middle::ty::{
1414 TypeVisitable , TypeVisitableExt ,
1515} ;
1616use rustc_session:: { declare_lint, declare_lint_pass} ;
17- use rustc_span:: def_id:: LocalDefId ;
17+ use rustc_span:: def_id:: { DefId , LocalDefId } ;
1818use rustc_span:: { Span , sym} ;
1919use tracing:: debug;
2020
@@ -360,7 +360,6 @@ impl<'tcx> FfiResult<'tcx> {
360360 /// if the note at their core reason is one in a provided list.
361361 /// If the FfiResult is not FfiUnsafe, or if no reasons are plucked,
362362 /// then return FfiSafe.
363- #[ expect( unused) ]
364363 fn take_with_core_note ( & mut self , notes : & [ DiagMessage ] ) -> Self {
365364 match self {
366365 Self :: FfiUnsafe ( this) => {
@@ -805,14 +804,30 @@ impl VisitorState {
805804/// and ``visit_*`` methods to recurse.
806805struct ImproperCTypesVisitor < ' a , ' tcx > {
807806 cx : & ' a LateContext < ' tcx > ,
807+ /// The module id of the item being checked for FFI-safety
808+ mod_id : DefId ,
808809 /// To prevent problems with recursive types,
809810 /// add a types-in-check cache.
810811 ty_cache : FxHashSet < Ty < ' tcx > > ,
811812}
812813
813814impl < ' a , ' tcx > ImproperCTypesVisitor < ' a , ' tcx > {
814- fn new ( cx : & ' a LateContext < ' tcx > ) -> Self {
815- Self { cx, ty_cache : FxHashSet :: default ( ) }
815+ fn new ( cx : & ' a LateContext < ' tcx > , mod_id : DefId ) -> Self {
816+ Self { cx, mod_id, ty_cache : FxHashSet :: default ( ) }
817+ }
818+
819+ /// Checks whether an uninhabited type (one without valid values) is safe-ish to have here.
820+ fn visit_uninhabited ( & self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
821+ if state. is_in_function_return ( ) {
822+ FfiResult :: FfiSafe
823+ } else {
824+ let desc = match ty. kind ( ) {
825+ ty:: Adt ( ..) => fluent:: lint_improper_ctypes_uninhabited_enum,
826+ ty:: Never => fluent:: lint_improper_ctypes_uninhabited_never,
827+ r @ _ => bug ! ( "unexpected ty_kind in uninhabited type handling: {:?}" , r) ,
828+ } ;
829+ FfiResult :: new_with_reason ( ty, desc, None )
830+ }
816831 }
817832
818833 /// Return the right help for Cstring and Cstr-linked unsafety.
@@ -948,6 +963,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
948963 if !matches ! ( def. adt_kind( ) , AdtKind :: Enum ) && def. repr ( ) . transparent ( ) {
949964 // determine if there is 0 or 1 non-1ZST field, and which it is.
950965 // (note: for enums, "transparent" means 1-variant)
966+ if !ty. is_inhabited_from ( self . cx . tcx , self . mod_id , self . cx . typing_env ( ) ) {
967+ // let's consider transparent structs to be maybe unsafe if uninhabited,
968+ // even if that is because of fields otherwise ignored in FFI-safety checks
969+ ffires_accumulator += variant
970+ . fields
971+ . iter ( )
972+ . map ( |field| {
973+ let field_ty = get_type_from_field ( self . cx , field, args) ;
974+ let mut field_res = self . visit_type ( state. get_next ( ty) , field_ty) ;
975+ field_res. take_with_core_note ( & [
976+ fluent:: lint_improper_ctypes_uninhabited_enum,
977+ fluent:: lint_improper_ctypes_uninhabited_never,
978+ ] )
979+ } )
980+ . reduce ( |r1, r2| r1 + r2)
981+ . unwrap ( ) // if uninhabited, then >0 fields
982+ }
951983 if let Some ( field) = super :: transparent_newtype_field ( self . cx . tcx , variant) {
952984 // Transparent newtypes have at most one non-ZST field which needs to be checked later
953985 ( false , vec ! [ field] )
@@ -1133,8 +1165,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11331165 use FfiResult :: * ;
11341166
11351167 if def. variants ( ) . is_empty ( ) {
1136- // Empty enums are okay... although sort of useless.
1137- return FfiSafe ;
1168+ // Empty enums are implicitly handled as the never type:
1169+ return self . visit_uninhabited ( state , ty ) ;
11381170 }
11391171 // Check for a repr() attribute to specify the size of the
11401172 // discriminant.
@@ -1190,18 +1222,33 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11901222 None ,
11911223 )
11921224 } else {
1193- let ffires = def
1225+ // small caveat to checking the variants: we authorise up to n-1 invariants
1226+ // to be unsafe because uninhabited.
1227+ // so for now let's isolate those unsafeties
1228+ let mut variants_uninhabited_ffires = vec ! [ FfiSafe ; def. variants( ) . len( ) ] ;
1229+
1230+ let mut ffires = def
11941231 . variants ( )
11951232 . iter ( )
1196- . map ( |variant| {
1197- let variant_res = self . visit_variant_fields ( state, ty, def, variant, args) ;
1233+ . enumerate ( )
1234+ . map ( |( variant_i, variant) | {
1235+ let mut variant_res = self . visit_variant_fields ( state, ty, def, variant, args) ;
1236+ variants_uninhabited_ffires[ variant_i] = variant_res. take_with_core_note ( & [
1237+ fluent:: lint_improper_ctypes_uninhabited_enum,
1238+ fluent:: lint_improper_ctypes_uninhabited_never,
1239+ ] ) ;
11981240 // FIXME(ctypes): check that enums allow any (up to all) variants to be phantoms?
11991241 // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?)
12001242 variant_res. forbid_phantom ( )
12011243 } )
12021244 . reduce ( |r1, r2| r1 + r2)
12031245 . unwrap ( ) ; // always at least one variant if we hit this branch
12041246
1247+ if variants_uninhabited_ffires. iter ( ) . all ( |res| matches ! ( res, FfiUnsafe ( ..) ) ) {
1248+ // if the enum is uninhabited, because all its variants are uninhabited
1249+ ffires += variants_uninhabited_ffires. into_iter ( ) . reduce ( |r1, r2| r1 + r2) . unwrap ( ) ;
1250+ }
1251+
12051252 // this enum is visited in the middle of another lint,
12061253 // so we override the "cause type" of the lint
12071254 // (for more detail, see comment in ``visit_struct_union`` before its call to ``ffires.with_overrides``)
@@ -1366,7 +1413,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13661413
13671414 ty:: Foreign ( ..) => FfiSafe ,
13681415
1369- ty:: Never => FfiSafe ,
1416+ ty:: Never => self . visit_uninhabited ( state , ty ) ,
13701417
13711418 // While opaque types are checked for earlier, if a projection in a struct field
13721419 // normalizes to an opaque type, then it will reach this branch.
@@ -1443,6 +1490,7 @@ impl<'tcx> ImproperCTypesLint {
14431490 current_depth : usize ,
14441491 depths : Vec < usize > ,
14451492 decls : Vec < & ' tcx hir:: FnDecl < ' tcx > > ,
1493+ hir_ids : Vec < hir:: HirId > ,
14461494 tys : Vec < Ty < ' tcx > > ,
14471495 }
14481496
@@ -1455,6 +1503,7 @@ impl<'tcx> ImproperCTypesLint {
14551503 {
14561504 self . decls . push ( * decl) ;
14571505 self . depths . push ( self . current_depth ) ;
1506+ self . hir_ids . push ( ty. hir_id ) ;
14581507 }
14591508
14601509 hir:: intravisit:: walk_ty ( self , ty) ;
@@ -1477,6 +1526,7 @@ impl<'tcx> ImproperCTypesLint {
14771526 }
14781527
14791528 let mut visitor = FnPtrFinder {
1529+ hir_ids : Vec :: new ( ) ,
14801530 tys : Vec :: new ( ) ,
14811531 decls : Vec :: new ( ) ,
14821532 depths : Vec :: new ( ) ,
@@ -1486,12 +1536,15 @@ impl<'tcx> ImproperCTypesLint {
14861536 visitor. visit_ty_unambig ( hir_ty) ;
14871537
14881538 let all_types = iter:: zip (
1489- visitor. depths . drain ( ..) ,
1539+ iter :: zip ( visitor. depths . drain ( ..) , visitor . hir_ids . drain ( .. ) ) ,
14901540 iter:: zip ( visitor. tys . drain ( ..) , visitor. decls . drain ( ..) ) ,
14911541 ) ;
1492- for ( depth, ( fn_ptr_ty, decl) ) in all_types {
1542+
1543+ for ( ( depth, hir_id) , ( fn_ptr_ty, decl) ) in all_types {
14931544 let mir_sig = get_fn_sig_from_mir_ty ( cx, fn_ptr_ty) ;
1494- self . check_foreign_fn ( cx, CItemKind :: Callback , mir_sig, decl, depth) ;
1545+ let mod_id = cx. tcx . parent_module ( hir_id) . to_def_id ( ) ;
1546+
1547+ self . check_foreign_fn ( cx, CItemKind :: Callback , mir_sig, decl, mod_id, depth) ;
14951548 }
14961549 }
14971550
@@ -1533,9 +1586,10 @@ impl<'tcx> ImproperCTypesLint {
15331586 }
15341587
15351588 /// Check that an extern "ABI" static variable is of a ffi-safe type.
1536- fn check_foreign_static ( & mut self , cx : & LateContext < ' tcx > , id : hir:: OwnerId , span : Span ) {
1537- let ty = cx. tcx . type_of ( id) . instantiate_identity ( ) ;
1538- let mut visitor = ImproperCTypesVisitor :: new ( cx) ;
1589+ fn check_foreign_static ( & mut self , cx : & LateContext < ' tcx > , id : hir:: HirId , span : Span ) {
1590+ let ty = cx. tcx . type_of ( id. owner ) . instantiate_identity ( ) ;
1591+ let mod_id = cx. tcx . parent_module ( id) . to_def_id ( ) ;
1592+ let mut visitor = ImproperCTypesVisitor :: new ( cx, mod_id) ;
15391593 let ffi_res = visitor. check_type ( VisitorState :: static_var ( ) , ty) ;
15401594 self . process_ffi_result ( cx, span, ffi_res, CItemKind :: ImportedExtern ) ;
15411595 }
@@ -1547,22 +1601,23 @@ impl<'tcx> ImproperCTypesLint {
15471601 fn_mode : CItemKind ,
15481602 sig : Sig < ' tcx > ,
15491603 decl : & ' tcx hir:: FnDecl < ' _ > ,
1604+ mod_id : DefId ,
15501605 depth : usize ,
15511606 ) {
15521607 let sig = cx. tcx . instantiate_bound_regions_with_erased ( sig) ;
15531608
15541609 for ( input_ty, input_hir) in iter:: zip ( sig. inputs ( ) , decl. inputs ) {
15551610 let mut state = VisitorState :: entry_point_from_fnmode ( fn_mode, FnPos :: Arg ) ;
15561611 state. depth = depth;
1557- let mut visitor = ImproperCTypesVisitor :: new ( cx) ;
1612+ let mut visitor = ImproperCTypesVisitor :: new ( cx, mod_id ) ;
15581613 let ffi_res = visitor. check_type ( state, * input_ty) ;
15591614 self . process_ffi_result ( cx, input_hir. span , ffi_res, fn_mode) ;
15601615 }
15611616
15621617 if let hir:: FnRetTy :: Return ( ret_hir) = decl. output {
15631618 let mut state = VisitorState :: entry_point_from_fnmode ( fn_mode, FnPos :: Ret ) ;
15641619 state. depth = depth;
1565- let mut visitor = ImproperCTypesVisitor :: new ( cx) ;
1620+ let mut visitor = ImproperCTypesVisitor :: new ( cx, mod_id ) ;
15661621 let ffi_res = visitor. check_type ( state, sig. output ( ) ) ;
15671622 self . process_ffi_result ( cx, ret_hir. span , ffi_res, fn_mode) ;
15681623 }
@@ -1690,12 +1745,13 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
16901745 // their surroundings, and their type is often declared inline
16911746 self . check_fn_for_external_abi_fnptr ( cx, it. owner_id . def_id , sig. decl ) ;
16921747 let mir_sig = cx. tcx . fn_sig ( it. owner_id . def_id ) . instantiate_identity ( ) ;
1748+ let mod_id = cx. tcx . parent_module_from_def_id ( it. owner_id . def_id ) . to_def_id ( ) ;
16931749 if !abi. is_rustic_abi ( ) {
1694- self . check_foreign_fn ( cx, CItemKind :: ImportedExtern , mir_sig, sig. decl , 0 ) ;
1750+ self . check_foreign_fn ( cx, CItemKind :: ImportedExtern , mir_sig, sig. decl , mod_id , 0 ) ;
16951751 }
16961752 }
16971753 hir:: ForeignItemKind :: Static ( ty, _, _) if !abi. is_rustic_abi ( ) => {
1698- self . check_foreign_static ( cx, it. owner_id , ty. span ) ;
1754+ self . check_foreign_static ( cx, it. hir_id ( ) , ty. span ) ;
16991755 }
17001756 hir:: ForeignItemKind :: Static ( ..) | hir:: ForeignItemKind :: Type => ( ) ,
17011757 }
@@ -1766,9 +1822,10 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
17661822 // "the element rendered unsafe" because their unsafety doesn't affect
17671823 // their surroundings, and their type is often declared inline
17681824 self . check_fn_for_external_abi_fnptr ( cx, id, decl) ;
1769- let mir_sig = cx. tcx . fn_sig ( id) . instantiate_identity ( ) ;
17701825 if !abi. is_rustic_abi ( ) {
1771- self . check_foreign_fn ( cx, CItemKind :: ExportedFunction , mir_sig, decl, 0 ) ;
1826+ let mir_sig = cx. tcx . fn_sig ( id) . instantiate_identity ( ) ;
1827+ let mod_id = cx. tcx . parent_module_from_def_id ( id) . to_def_id ( ) ;
1828+ self . check_foreign_fn ( cx, CItemKind :: ExportedFunction , mir_sig, decl, mod_id, 0 ) ;
17721829 }
17731830 }
17741831}
0 commit comments