@@ -11,8 +11,8 @@ use rustc_hir::intravisit::VisitorExt;
1111use rustc_hir:: { self as hir, AmbigArg } ;
1212use rustc_middle:: bug;
1313use rustc_middle:: ty:: {
14- self , Adt , AdtDef , AdtKind , GenericArgsRef , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable ,
15- TypeVisitableExt ,
14+ self , Adt , AdtDef , AdtKind , Binder , FnSig , GenericArgsRef , Ty , TyCtxt , TypeSuperVisitable ,
15+ TypeVisitable , TypeVisitableExt ,
1616} ;
1717use rustc_session:: { declare_lint, declare_lint_pass} ;
1818use rustc_span:: def_id:: LocalDefId ;
@@ -170,6 +170,8 @@ declare_lint_pass!(ImproperCTypesLint => [
170170 USES_POWER_ALIGNMENT ,
171171] ) ;
172172
173+ type Sig < ' tcx > = Binder < ' tcx , FnSig < ' tcx > > ;
174+
173175/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
174176#[ inline]
175177fn get_type_from_field < ' tcx > (
@@ -303,7 +305,6 @@ impl<'tcx> FfiResult<'tcx> {
303305 }
304306 /// If the FfiPhantom variant, turns it into a FfiUnsafe version.
305307 /// Otherwise, keep unchanged.
306- #[ expect( unused) ]
307308 fn forbid_phantom ( self ) -> Self {
308309 match self {
309310 Self :: FfiPhantom ( ty) => {
@@ -575,6 +576,7 @@ impl VisitorState {
575576
576577 // FIXME(const): ideally .bits() and ::from_bits() wouldn't be necessary.
577578 // Unfortunately, "cannot call non-const operator in constants" (bitwise or?).
579+ const NONE : Self = Self :: empty ( ) ;
578580 const STATIC_TY : Self = Self :: STATIC ;
579581 const ARGUMENT_TY_IN_DEFINITION : Self =
580582 Self :: from_bits ( Self :: FUNC . bits ( ) | Self :: DEFINED . bits ( ) ) . unwrap ( ) ;
@@ -686,6 +688,41 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
686688 }
687689 }
688690
691+ /// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call.
692+ fn visit_fnptr (
693+ & self ,
694+ _state : VisitorState ,
695+ _outer_ty : Option < Ty < ' tcx > > ,
696+ ty : Ty < ' tcx > ,
697+ sig : Sig < ' tcx > ,
698+ ) -> FfiResult < ' tcx > {
699+ use FfiResult :: * ;
700+ debug_assert ! ( !sig. abi( ) . is_rustic_abi( ) ) ;
701+
702+ let sig = self . cx . tcx . instantiate_bound_regions_with_erased ( sig) ;
703+
704+ let mut all_ffires = FfiSafe ;
705+
706+ for arg in sig. inputs ( ) {
707+ let ffi_res = self . visit_type ( VisitorState :: ARGUMENT_TY_IN_FNPTR , Some ( ty) , * arg) ;
708+ all_ffires += ffi_res. forbid_phantom ( ) . wrap_all (
709+ ty,
710+ fluent:: lint_improper_ctypes_fnptr_indirect_reason,
711+ None ,
712+ ) ;
713+ }
714+
715+ let ret_ty = sig. output ( ) ;
716+
717+ let ffi_res = self . visit_type ( VisitorState :: RETURN_TY_IN_FNPTR , Some ( ty) , ret_ty) ;
718+ all_ffires += ffi_res. forbid_phantom ( ) . wrap_all (
719+ ty,
720+ fluent:: lint_improper_ctypes_fnptr_indirect_reason,
721+ None ,
722+ ) ;
723+ all_ffires
724+ }
725+
689726 /// Checks if a simple numeric (int, float) type has an actual portable definition
690727 /// for the compile target.
691728 fn visit_numeric ( & self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
@@ -1101,27 +1138,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11011138 }
11021139 }
11031140
1141+ // fnptrs are a special case, they always need to be treated as
1142+ // "the element rendered unsafe" because their unsafety doesn't affect
1143+ // their surroundings, and their type is often declared inline
1144+ // as a result, don't go into them when scanning for the safety of something else
11041145 ty:: FnPtr ( sig_tys, hdr) => {
11051146 let sig = sig_tys. with ( hdr) ;
11061147 if sig. abi ( ) . is_rustic_abi ( ) {
1107- return FfiResult :: new_with_reason (
1148+ FfiResult :: new_with_reason (
11081149 ty,
11091150 fluent:: lint_improper_ctypes_fnptr_reason,
11101151 Some ( fluent:: lint_improper_ctypes_fnptr_help) ,
1111- ) ;
1112- }
1113-
1114- let sig = tcx. instantiate_bound_regions_with_erased ( sig) ;
1115- for arg in sig. inputs ( ) {
1116- match self . visit_type ( VisitorState :: ARGUMENT_TY_IN_FNPTR , Some ( ty) , * arg) {
1117- FfiSafe => { }
1118- r => return r,
1119- }
1152+ )
1153+ } else {
1154+ FfiSafe
11201155 }
1121-
1122- let ret_ty = sig. output ( ) ;
1123-
1124- self . visit_type ( VisitorState :: RETURN_TY_IN_FNPTR , Some ( ty) , ret_ty)
11251156 }
11261157
11271158 ty:: Foreign ( ..) => FfiSafe ,
@@ -1189,10 +1220,31 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11891220 return res;
11901221 }
11911222 }
1192-
11931223 self . visit_type ( state, None , ty)
11941224 }
11951225
1226+ /// Checks the FFI-safety of a callback (`extern "ABI"` FnPtr)
1227+ /// that is found in a no-FFI-safety-needed context.
1228+ fn check_fnptr ( & self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
1229+ let ty = self . cx . tcx . try_normalize_erasing_regions ( self . cx . typing_env ( ) , ty) . unwrap_or ( ty) ;
1230+
1231+ match * ty. kind ( ) {
1232+ ty:: FnPtr ( sig_tys, hdr) => {
1233+ let sig = sig_tys. with ( hdr) ;
1234+ if sig. abi ( ) . is_rustic_abi ( ) {
1235+ bug ! (
1236+ "expected to inspect the type of an `extern \" ABI\" ` FnPtr, not an internal-ABI one"
1237+ )
1238+ } else {
1239+ self . visit_fnptr ( VisitorState :: NONE , None , ty, sig)
1240+ }
1241+ }
1242+ r @ _ => {
1243+ bug ! ( "expected to inspect the type of an `extern \" ABI\" ` FnPtr, not {:?}" , r, )
1244+ }
1245+ }
1246+ }
1247+
11961248 /// Per-struct-field function that checks if a struct definition follows
11971249 /// the Power alignment Rule (see the `check_struct_for_power_alignment` method).
11981250 fn check_arg_for_power_alignment ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
@@ -1270,7 +1322,6 @@ impl<'tcx> ImproperCTypesLint {
12701322 fn check_type_for_external_abi_fnptr (
12711323 & mut self ,
12721324 cx : & LateContext < ' tcx > ,
1273- state : VisitorState ,
12741325 hir_ty : & hir:: Ty < ' tcx > ,
12751326 ty : Ty < ' tcx > ,
12761327 ) {
@@ -1313,8 +1364,7 @@ impl<'tcx> ImproperCTypesLint {
13131364 let all_types = iter:: zip ( visitor. tys . drain ( ..) , visitor. spans . drain ( ..) ) ;
13141365 for ( fn_ptr_ty, span) in all_types {
13151366 let visitor = ImproperCTypesVisitor :: new ( cx) ;
1316- // FIXME(ctypes): make a check_for_fnptr
1317- let ffi_res = visitor. check_type ( state, fn_ptr_ty) ;
1367+ let ffi_res = visitor. check_fnptr ( fn_ptr_ty) ;
13181368
13191369 self . process_ffi_result ( cx, span, ffi_res, CItemKind :: Callback ) ;
13201370 }
@@ -1325,21 +1375,18 @@ impl<'tcx> ImproperCTypesLint {
13251375 fn check_fn_for_external_abi_fnptr (
13261376 & mut self ,
13271377 cx : & LateContext < ' tcx > ,
1328- fn_mode : CItemKind ,
13291378 def_id : LocalDefId ,
13301379 decl : & ' tcx hir:: FnDecl < ' _ > ,
13311380 ) {
13321381 let sig = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) ;
13331382 let sig = cx. tcx . instantiate_bound_regions_with_erased ( sig) ;
13341383
13351384 for ( input_ty, input_hir) in iter:: zip ( sig. inputs ( ) , decl. inputs ) {
1336- let state = VisitorState :: argument_from_fnmode ( fn_mode) ;
1337- self . check_type_for_external_abi_fnptr ( cx, state, input_hir, * input_ty) ;
1385+ self . check_type_for_external_abi_fnptr ( cx, input_hir, * input_ty) ;
13381386 }
13391387
13401388 if let hir:: FnRetTy :: Return ( ret_hir) = decl. output {
1341- let state = VisitorState :: return_from_fnmode ( fn_mode) ;
1342- self . check_type_for_external_abi_fnptr ( cx, state, ret_hir, sig. output ( ) ) ;
1389+ self . check_type_for_external_abi_fnptr ( cx, ret_hir, sig. output ( ) ) ;
13431390 }
13441391 }
13451392
@@ -1360,6 +1407,7 @@ impl<'tcx> ImproperCTypesLint {
13601407 ImproperCTypesVisitor :: check_struct_for_power_alignment ( cx, item, adt_def) ;
13611408 }
13621409
1410+ /// Check that an extern "ABI" static variable is of a ffi-safe type.
13631411 fn check_foreign_static ( & self , cx : & LateContext < ' tcx > , id : hir:: OwnerId , span : Span ) {
13641412 let ty = cx. tcx . type_of ( id) . instantiate_identity ( ) ;
13651413 let visitor = ImproperCTypesVisitor :: new ( cx) ;
@@ -1512,20 +1560,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
15121560 // fnptrs are a special case, they always need to be treated as
15131561 // "the element rendered unsafe" because their unsafety doesn't affect
15141562 // their surroundings, and their type is often declared inline
1563+ self . check_fn_for_external_abi_fnptr ( cx, it. owner_id . def_id , sig. decl ) ;
15151564 if !abi. is_rustic_abi ( ) {
15161565 self . check_foreign_fn (
15171566 cx,
15181567 CItemKind :: ImportedExtern ,
15191568 it. owner_id . def_id ,
15201569 sig. decl ,
15211570 ) ;
1522- } else {
1523- self . check_fn_for_external_abi_fnptr (
1524- cx,
1525- CItemKind :: ImportedExtern ,
1526- it. owner_id . def_id ,
1527- sig. decl ,
1528- ) ;
15291571 }
15301572 }
15311573 hir:: ForeignItemKind :: Static ( ty, _, _) if !abi. is_rustic_abi ( ) => {
@@ -1542,7 +1584,6 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
15421584 | hir:: ItemKind :: TyAlias ( _, _, ty) => {
15431585 self . check_type_for_external_abi_fnptr (
15441586 cx,
1545- VisitorState :: STATIC_TY ,
15461587 ty,
15471588 cx. tcx . type_of ( item. owner_id ) . instantiate_identity ( ) ,
15481589 ) ;
@@ -1575,7 +1616,6 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
15751616 fn check_field_def ( & mut self , cx : & LateContext < ' tcx > , field : & ' tcx hir:: FieldDef < ' tcx > ) {
15761617 self . check_type_for_external_abi_fnptr (
15771618 cx,
1578- VisitorState :: STATIC_TY ,
15791619 field. ty ,
15801620 cx. tcx . type_of ( field. def_id ) . instantiate_identity ( ) ,
15811621 ) ;
@@ -1601,10 +1641,9 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
16011641 // fnptrs are a special case, they always need to be treated as
16021642 // "the element rendered unsafe" because their unsafety doesn't affect
16031643 // their surroundings, and their type is often declared inline
1644+ self . check_fn_for_external_abi_fnptr ( cx, id, decl) ;
16041645 if !abi. is_rustic_abi ( ) {
16051646 self . check_foreign_fn ( cx, CItemKind :: ExportedFunction , id, decl) ;
1606- } else {
1607- self . check_fn_for_external_abi_fnptr ( cx, CItemKind :: ExportedFunction , id, decl) ;
16081647 }
16091648 }
16101649}
0 commit comments