@@ -10,8 +10,8 @@ use rustc_hir::intravisit::VisitorExt;
1010use rustc_hir:: { self as hir, AmbigArg } ;
1111use rustc_middle:: bug;
1212use rustc_middle:: ty:: {
13- self , Adt , AdtDef , AdtKind , GenericArgsRef , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable ,
14- TypeVisitableExt ,
13+ self , Adt , AdtDef , AdtKind , Binder , FnSig , GenericArgsRef , Ty , TyCtxt , TypeSuperVisitable ,
14+ TypeVisitable , TypeVisitableExt ,
1515} ;
1616use rustc_session:: { declare_lint, declare_lint_pass} ;
1717use rustc_span:: def_id:: LocalDefId ;
@@ -169,6 +169,8 @@ declare_lint_pass!(ImproperCTypesLint => [
169169 USES_POWER_ALIGNMENT ,
170170] ) ;
171171
172+ type Sig < ' tcx > = Binder < ' tcx , FnSig < ' tcx > > ;
173+
172174/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
173175#[ inline]
174176fn get_type_from_field < ' tcx > (
@@ -372,7 +374,6 @@ impl<'tcx> FfiResult<'tcx> {
372374 }
373375 /// If the FfiPhantom variant, turns it into a FfiUnsafe version.
374376 /// Otherwise, keep unchanged.
375- #[ expect( unused) ]
376377 fn forbid_phantom ( self ) -> Self {
377378 match self {
378379 Self :: FfiPhantom ( ty) => {
@@ -728,16 +729,17 @@ impl VisitorState {
728729 depth : self . depth + 1 ,
729730 }
730731 }
731- fn get_next_in_fnptr < ' tcx > ( & self , current_ty : Ty < ' tcx > , is_ret : bool ) -> Self {
732- debug_assert ! ( matches!( current_ty. kind( ) , ty:: FnPtr ( ..) ) ) ;
732+
733+ /// Generate the state for an "outermost" type to a FnPtr
734+ fn fnptr_entry_point ( depth : usize , is_ret : bool ) -> Self {
733735 Self {
736+ depth,
734737 persistent_flags : if is_ret {
735738 PersistentStateFlags :: RETURN_TY_IN_FNPTR
736739 } else {
737740 PersistentStateFlags :: ARGUMENT_TY_IN_FNPTR
738741 } ,
739- ephemeral_flags : EphemeralStateFlags :: from_outer_ty ( current_ty) ,
740- depth : self . depth + 1 ,
742+ ephemeral_flags : EphemeralStateFlags :: NO_OUTER_TY | EphemeralStateFlags :: NOOUT_FNPTR ,
741743 }
742744 }
743745
@@ -858,6 +860,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
858860 Self { cx, ty_cache : FxHashSet :: default ( ) }
859861 }
860862
863+ /// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call.
864+ fn visit_fnptr ( & mut self , depth : usize , ty : Ty < ' tcx > , sig : Sig < ' tcx > ) -> FfiResult < ' tcx > {
865+ use FfiResult :: * ;
866+ debug_assert ! ( !sig. abi( ) . is_rustic_abi( ) ) ;
867+
868+ let sig = self . cx . tcx . instantiate_bound_regions_with_erased ( sig) ;
869+
870+ let mut all_ffires = FfiSafe ;
871+
872+ for arg in sig. inputs ( ) {
873+ let ffi_res = self . visit_type ( VisitorState :: fnptr_entry_point ( depth, false ) , * arg) ;
874+ all_ffires += ffi_res. forbid_phantom ( ) . wrap_all (
875+ ty,
876+ fluent:: lint_improper_ctypes_fnptr_indirect_reason,
877+ None ,
878+ ) ;
879+ }
880+
881+ let ret_ty = sig. output ( ) ;
882+
883+ let ffi_res = self . visit_type ( VisitorState :: fnptr_entry_point ( depth, true ) , ret_ty) ;
884+ all_ffires += ffi_res. forbid_phantom ( ) . wrap_all (
885+ ty,
886+ fluent:: lint_improper_ctypes_fnptr_indirect_reason,
887+ None ,
888+ ) ;
889+ all_ffires
890+ }
891+
861892 /// Return the right help for Cstring and Cstr-linked unsafety.
862893 fn visit_cstr ( & mut self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
863894 debug_assert ! ( matches!( ty. kind( ) , ty:: Adt ( def, _)
@@ -1246,26 +1277,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12461277 }
12471278 }
12481279
1280+ // fnptrs are a special case, they always need to be treated as
1281+ // "the element rendered unsafe" because their unsafety doesn't affect
1282+ // their surroundings, and their type is often declared inline
1283+ // as a result, don't go into them when scanning for the safety of something else
12491284 ty:: FnPtr ( sig_tys, hdr) => {
12501285 let sig = sig_tys. with ( hdr) ;
12511286 if sig. abi ( ) . is_rustic_abi ( ) {
1252- return FfiResult :: new_with_reason (
1287+ FfiResult :: new_with_reason (
12531288 ty,
12541289 fluent:: lint_improper_ctypes_fnptr_reason,
12551290 Some ( fluent:: lint_improper_ctypes_fnptr_help) ,
1256- ) ;
1257- }
1258-
1259- let sig = tcx. instantiate_bound_regions_with_erased ( sig) ;
1260- for arg in sig. inputs ( ) {
1261- match self . visit_type ( state. get_next_in_fnptr ( ty, false ) , * arg) {
1262- FfiSafe => { }
1263- r => return r,
1264- }
1291+ )
1292+ } else {
1293+ FfiSafe
12651294 }
1266-
1267- let ret_ty = sig. output ( ) ;
1268- self . visit_type ( state. get_next_in_fnptr ( ty, true ) , ret_ty)
12691295 }
12701296
12711297 ty:: Foreign ( ..) => FfiSafe ,
@@ -1330,9 +1356,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
13301356 if let Some ( res) = self . visit_for_opaque_ty ( ty) {
13311357 return res;
13321358 }
1333-
13341359 self . visit_type ( state, ty)
13351360 }
1361+
1362+ /// Checks the FFI-safety of a callback (`extern "ABI"` FnPtr)
1363+ /// that is found in a no-FFI-safety-needed context.
1364+ fn check_fnptr ( & mut self , depth : usize , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
1365+ let ty = self . cx . tcx . try_normalize_erasing_regions ( self . cx . typing_env ( ) , ty) . unwrap_or ( ty) ;
1366+
1367+ match * ty. kind ( ) {
1368+ ty:: FnPtr ( sig_tys, hdr) => {
1369+ let sig = sig_tys. with ( hdr) ;
1370+ if sig. abi ( ) . is_rustic_abi ( ) {
1371+ bug ! (
1372+ "expected to inspect the type of an `extern \" ABI\" ` FnPtr, not an internal-ABI one"
1373+ )
1374+ } else {
1375+ self . visit_fnptr ( depth, ty, sig)
1376+ }
1377+ }
1378+ r @ _ => {
1379+ bug ! ( "expected to inspect the type of an `extern \" ABI\" ` FnPtr, not {:?}" , r, )
1380+ }
1381+ }
1382+ }
13361383}
13371384
13381385impl < ' tcx > ImproperCTypesLint {
@@ -1341,7 +1388,6 @@ impl<'tcx> ImproperCTypesLint {
13411388 fn check_type_for_external_abi_fnptr (
13421389 & mut self ,
13431390 cx : & LateContext < ' tcx > ,
1344- state : VisitorState ,
13451391 hir_ty : & hir:: Ty < ' tcx > ,
13461392 ty : Ty < ' tcx > ,
13471393 ) {
@@ -1397,9 +1443,7 @@ impl<'tcx> ImproperCTypesLint {
13971443 ) ;
13981444 for ( depth, ( fn_ptr_ty, span) ) in all_types {
13991445 let mut visitor = ImproperCTypesVisitor :: new ( cx) ;
1400- let bridge_state = VisitorState { depth, ..state } ;
1401- // FIXME(ctypes): make a check_for_fnptr
1402- let ffi_res = visitor. check_type ( bridge_state, fn_ptr_ty) ;
1446+ let ffi_res = visitor. check_fnptr ( depth, fn_ptr_ty) ;
14031447
14041448 self . process_ffi_result ( cx, span, ffi_res, CItemKind :: Callback ) ;
14051449 }
@@ -1410,21 +1454,18 @@ impl<'tcx> ImproperCTypesLint {
14101454 fn check_fn_for_external_abi_fnptr (
14111455 & mut self ,
14121456 cx : & LateContext < ' tcx > ,
1413- fn_mode : CItemKind ,
14141457 def_id : LocalDefId ,
14151458 decl : & ' tcx hir:: FnDecl < ' _ > ,
14161459 ) {
14171460 let sig = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) ;
14181461 let sig = cx. tcx . instantiate_bound_regions_with_erased ( sig) ;
14191462
14201463 for ( input_ty, input_hir) in iter:: zip ( sig. inputs ( ) , decl. inputs ) {
1421- let state = VisitorState :: argument_from_fnmode ( fn_mode) ;
1422- self . check_type_for_external_abi_fnptr ( cx, state, input_hir, * input_ty) ;
1464+ self . check_type_for_external_abi_fnptr ( cx, input_hir, * input_ty) ;
14231465 }
14241466
14251467 if let hir:: FnRetTy :: Return ( ret_hir) = decl. output {
1426- let state = VisitorState :: return_from_fnmode ( fn_mode) ;
1427- self . check_type_for_external_abi_fnptr ( cx, state, ret_hir, sig. output ( ) ) ;
1468+ self . check_type_for_external_abi_fnptr ( cx, ret_hir, sig. output ( ) ) ;
14281469 }
14291470 }
14301471
@@ -1445,6 +1486,7 @@ impl<'tcx> ImproperCTypesLint {
14451486 check_struct_for_power_alignment ( cx, item, adt_def) ;
14461487 }
14471488
1489+ /// Check that an extern "ABI" static variable is of a ffi-safe type.
14481490 fn check_foreign_static ( & mut self , cx : & LateContext < ' tcx > , id : hir:: OwnerId , span : Span ) {
14491491 let ty = cx. tcx . type_of ( id) . instantiate_identity ( ) ;
14501492 let mut visitor = ImproperCTypesVisitor :: new ( cx) ;
@@ -1597,20 +1639,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
15971639 // fnptrs are a special case, they always need to be treated as
15981640 // "the element rendered unsafe" because their unsafety doesn't affect
15991641 // their surroundings, and their type is often declared inline
1642+ self . check_fn_for_external_abi_fnptr ( cx, it. owner_id . def_id , sig. decl ) ;
16001643 if !abi. is_rustic_abi ( ) {
16011644 self . check_foreign_fn (
16021645 cx,
16031646 CItemKind :: ImportedExtern ,
16041647 it. owner_id . def_id ,
16051648 sig. decl ,
16061649 ) ;
1607- } else {
1608- self . check_fn_for_external_abi_fnptr (
1609- cx,
1610- CItemKind :: ImportedExtern ,
1611- it. owner_id . def_id ,
1612- sig. decl ,
1613- ) ;
16141650 }
16151651 }
16161652 hir:: ForeignItemKind :: Static ( ty, _, _) if !abi. is_rustic_abi ( ) => {
@@ -1627,7 +1663,6 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
16271663 | hir:: ItemKind :: TyAlias ( _, _, ty) => {
16281664 self . check_type_for_external_abi_fnptr (
16291665 cx,
1630- VisitorState :: static_var ( ) ,
16311666 ty,
16321667 cx. tcx . type_of ( item. owner_id ) . instantiate_identity ( ) ,
16331668 ) ;
@@ -1660,7 +1695,6 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
16601695 fn check_field_def ( & mut self , cx : & LateContext < ' tcx > , field : & ' tcx hir:: FieldDef < ' tcx > ) {
16611696 self . check_type_for_external_abi_fnptr (
16621697 cx,
1663- VisitorState :: static_var ( ) ,
16641698 field. ty ,
16651699 cx. tcx . type_of ( field. def_id ) . instantiate_identity ( ) ,
16661700 ) ;
@@ -1686,10 +1720,9 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
16861720 // fnptrs are a special case, they always need to be treated as
16871721 // "the element rendered unsafe" because their unsafety doesn't affect
16881722 // their surroundings, and their type is often declared inline
1723+ self . check_fn_for_external_abi_fnptr ( cx, id, decl) ;
16891724 if !abi. is_rustic_abi ( ) {
16901725 self . check_foreign_fn ( cx, CItemKind :: ExportedFunction , id, decl) ;
1691- } else {
1692- self . check_fn_for_external_abi_fnptr ( cx, CItemKind :: ExportedFunction , id, decl) ;
16931726 }
16941727 }
16951728}
0 commit comments