@@ -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 ;
@@ -23,6 +23,8 @@ use super::repr_nullable_ptr;
2323use crate :: lints:: { ImproperCTypes , ImproperCTypesLayer , UsesPowerAlignment } ;
2424use crate :: { LateContext , LateLintPass , LintContext , fluent_generated as fluent} ;
2525
26+ type Sig < ' tcx > = Binder < ' tcx , FnSig < ' tcx > > ;
27+
2628/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
2729#[ inline]
2830fn get_type_from_field < ' tcx > (
@@ -156,7 +158,6 @@ impl<'tcx> FfiResult<'tcx> {
156158 }
157159 /// If the FfiPhantom variant, turns it into a FfiUnsafe version.
158160 /// Otherwise, keep unchanged.
159- #[ expect( unused) ]
160161 fn forbid_phantom ( self ) -> Self {
161162 match self {
162163 Self :: FfiPhantom ( ty) => {
@@ -528,6 +529,41 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
528529 }
529530 }
530531
532+ /// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call.
533+ fn visit_fnptr (
534+ & self ,
535+ _state : VisitorState ,
536+ _outer_ty : Option < Ty < ' tcx > > ,
537+ ty : Ty < ' tcx > ,
538+ sig : Sig < ' tcx > ,
539+ ) -> FfiResult < ' tcx > {
540+ use FfiResult :: * ;
541+ debug_assert ! ( !sig. abi( ) . is_rustic_abi( ) ) ;
542+
543+ let sig = self . cx . tcx . instantiate_bound_regions_with_erased ( sig) ;
544+
545+ let mut all_ffires = FfiSafe ;
546+
547+ for arg in sig. inputs ( ) {
548+ let ffi_res = self . visit_type ( VisitorState :: ArgumentTyInFnPtr , Some ( ty) , * arg) ;
549+ all_ffires += ffi_res. forbid_phantom ( ) . wrap_all (
550+ ty,
551+ fluent:: lint_improper_ctypes_fnptr_indirect_reason,
552+ None ,
553+ ) ;
554+ }
555+
556+ let ret_ty = sig. output ( ) ;
557+
558+ let ffi_res = self . visit_type ( VisitorState :: ReturnTyInFnPtr , Some ( ty) , ret_ty) ;
559+ all_ffires += ffi_res. forbid_phantom ( ) . wrap_all (
560+ ty,
561+ fluent:: lint_improper_ctypes_fnptr_indirect_reason,
562+ None ,
563+ ) ;
564+ all_ffires
565+ }
566+
531567 /// Checks if a simple numeric (int, float) type has an actual portable definition
532568 /// for the compile target.
533569 fn visit_numeric ( & self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
@@ -955,27 +991,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
955991 }
956992 }
957993
994+ // fnptrs are a special case, they always need to be treated as
995+ // "the element rendered unsafe" because their unsafety doesn't affect
996+ // their surroundings, and their type is often declared inline
997+ // as a result, don't go into them when scanning for the safety of something else
958998 ty:: FnPtr ( sig_tys, hdr) => {
959999 let sig = sig_tys. with ( hdr) ;
9601000 if sig. abi ( ) . is_rustic_abi ( ) {
961- return FfiResult :: new_with_reason (
1001+ FfiResult :: new_with_reason (
9621002 ty,
9631003 fluent:: lint_improper_ctypes_fnptr_reason,
9641004 Some ( fluent:: lint_improper_ctypes_fnptr_help) ,
965- ) ;
966- }
967-
968- let sig = tcx. instantiate_bound_regions_with_erased ( sig) ;
969- for arg in sig. inputs ( ) {
970- match self . visit_type ( VisitorState :: ArgumentTyInFnPtr , Some ( ty) , * arg) {
971- FfiSafe => { }
972- r => return r,
973- }
1005+ )
1006+ } else {
1007+ FfiSafe
9741008 }
975-
976- let ret_ty = sig. output ( ) ;
977-
978- self . visit_type ( VisitorState :: ReturnTyInFnPtr , Some ( ty) , ret_ty)
9791009 }
9801010
9811011 ty:: Foreign ( ..) => FfiSafe ,
@@ -1046,10 +1076,29 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10461076 return res;
10471077 }
10481078 }
1049-
10501079 self . visit_type ( state, None , ty)
10511080 }
10521081
1082+ fn check_for_fnptr ( & self , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
1083+ let ty = self . cx . tcx . try_normalize_erasing_regions ( self . cx . typing_env ( ) , ty) . unwrap_or ( ty) ;
1084+
1085+ match * ty. kind ( ) {
1086+ ty:: FnPtr ( sig_tys, hdr) => {
1087+ let sig = sig_tys. with ( hdr) ;
1088+ if sig. abi ( ) . is_rustic_abi ( ) {
1089+ bug ! (
1090+ "expected to inspect the type of an `extern \" ABI\" ` FnPtr, not an internal-ABI one"
1091+ )
1092+ } else {
1093+ self . visit_fnptr ( VisitorState :: None , None , ty, sig)
1094+ }
1095+ }
1096+ r @ _ => {
1097+ bug ! ( "expected to inspect the type of an `extern \" ABI\" ` FnPtr, not {:?}" , r, )
1098+ }
1099+ }
1100+ }
1101+
10531102 fn check_arg_for_power_alignment ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
10541103 let tcx = cx. tcx ;
10551104 assert ! ( tcx. sess. target. os == "aix" ) ;
@@ -1117,7 +1166,6 @@ impl<'tcx> ImproperCTypesLint {
11171166 fn check_type_for_external_abi_fnptr (
11181167 & mut self ,
11191168 cx : & LateContext < ' tcx > ,
1120- state : VisitorState ,
11211169 hir_ty : & hir:: Ty < ' tcx > ,
11221170 ty : Ty < ' tcx > ,
11231171 ) {
@@ -1160,8 +1208,7 @@ impl<'tcx> ImproperCTypesLint {
11601208 let all_types = iter:: zip ( visitor. tys . drain ( ..) , visitor. spans . drain ( ..) ) ;
11611209 all_types. for_each ( |( fn_ptr_ty, span) | {
11621210 let visitor = ImproperCTypesVisitor :: new ( cx) ;
1163- // TODO: make a check_for_fnptr
1164- let ffi_res = visitor. check_for_type ( state, fn_ptr_ty) ;
1211+ let ffi_res = visitor. check_for_fnptr ( fn_ptr_ty) ;
11651212
11661213 self . process_ffi_result ( cx, span, ffi_res, CItemKind :: Callback )
11671214 } ) ;
@@ -1172,21 +1219,18 @@ impl<'tcx> ImproperCTypesLint {
11721219 fn check_fn_for_external_abi_fnptr (
11731220 & mut self ,
11741221 cx : & LateContext < ' tcx > ,
1175- fn_mode : CItemKind ,
11761222 def_id : LocalDefId ,
11771223 decl : & ' tcx hir:: FnDecl < ' _ > ,
11781224 ) {
11791225 let sig = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) ;
11801226 let sig = cx. tcx . instantiate_bound_regions_with_erased ( sig) ;
11811227
11821228 for ( input_ty, input_hir) in iter:: zip ( sig. inputs ( ) , decl. inputs ) {
1183- let state = VisitorState :: argument_from_fnmode ( fn_mode) ;
1184- self . check_type_for_external_abi_fnptr ( cx, state, input_hir, * input_ty) ;
1229+ self . check_type_for_external_abi_fnptr ( cx, input_hir, * input_ty) ;
11851230 }
11861231
11871232 if let hir:: FnRetTy :: Return ( ret_hir) = decl. output {
1188- let state = VisitorState :: return_from_fnmode ( fn_mode) ;
1189- self . check_type_for_external_abi_fnptr ( cx, state, ret_hir, sig. output ( ) ) ;
1233+ self . check_type_for_external_abi_fnptr ( cx, ret_hir, sig. output ( ) ) ;
11901234 }
11911235 }
11921236
@@ -1207,6 +1251,7 @@ impl<'tcx> ImproperCTypesLint {
12071251 ImproperCTypesVisitor :: check_struct_for_power_alignment ( cx, item, adt_def) ;
12081252 }
12091253
1254+ /// Check that an extern "ABI" static variable is of a ffi-safe type.
12101255 fn check_foreign_static ( & self , cx : & LateContext < ' tcx > , id : hir:: OwnerId , span : Span ) {
12111256 let ty = cx. tcx . type_of ( id) . instantiate_identity ( ) ;
12121257 let visitor = ImproperCTypesVisitor :: new ( cx) ;
@@ -1359,20 +1404,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
13591404 // fnptrs are a special case, they always need to be treated as
13601405 // "the element rendered unsafe" because their unsafety doesn't affect
13611406 // their surroundings, and their type is often declared inline
1407+ self . check_fn_for_external_abi_fnptr ( cx, it. owner_id . def_id , sig. decl ) ;
13621408 if !abi. is_rustic_abi ( ) {
13631409 self . check_foreign_fn (
13641410 cx,
13651411 CItemKind :: ImportedExtern ,
13661412 it. owner_id . def_id ,
13671413 sig. decl ,
13681414 ) ;
1369- } else {
1370- self . check_fn_for_external_abi_fnptr (
1371- cx,
1372- CItemKind :: ImportedExtern ,
1373- it. owner_id . def_id ,
1374- sig. decl ,
1375- ) ;
13761415 }
13771416 }
13781417 hir:: ForeignItemKind :: Static ( ty, _, _) if !abi. is_rustic_abi ( ) => {
@@ -1389,7 +1428,6 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
13891428 | hir:: ItemKind :: TyAlias ( _, _, ty) => {
13901429 self . check_type_for_external_abi_fnptr (
13911430 cx,
1392- VisitorState :: StaticTy ,
13931431 ty,
13941432 cx. tcx . type_of ( item. owner_id ) . instantiate_identity ( ) ,
13951433 ) ;
@@ -1422,7 +1460,6 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
14221460 fn check_field_def ( & mut self , cx : & LateContext < ' tcx > , field : & ' tcx hir:: FieldDef < ' tcx > ) {
14231461 self . check_type_for_external_abi_fnptr (
14241462 cx,
1425- VisitorState :: StaticTy ,
14261463 field. ty ,
14271464 cx. tcx . type_of ( field. def_id ) . instantiate_identity ( ) ,
14281465 ) ;
@@ -1448,10 +1485,9 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
14481485 // fnptrs are a special case, they always need to be treated as
14491486 // "the element rendered unsafe" because their unsafety doesn't affect
14501487 // their surroundings, and their type is often declared inline
1488+ self . check_fn_for_external_abi_fnptr ( cx, id, decl) ;
14511489 if !abi. is_rustic_abi ( ) {
14521490 self . check_foreign_fn ( cx, CItemKind :: ExportedFunction , id, decl) ;
1453- } else {
1454- self . check_fn_for_external_abi_fnptr ( cx, CItemKind :: ExportedFunction , id, decl) ;
14551491 }
14561492 }
14571493}
0 commit comments