Skip to content

Commit c6eb386

Browse files
committed
ImproperCTypes: change handling of FnPtrs
Notably, those FnPtrs are treated as "the item impacted by the error", instead of the functions/structs making use of them.
1 parent 218c43c commit c6eb386

File tree

5 files changed

+106
-75
lines changed

5 files changed

+106
-75
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,9 @@ lint_improper_ctypes_enum_repr_help =
376376
lint_improper_ctypes_enum_repr_reason = enum has no representation hint
377377
lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead
378378
379+
lint_improper_ctypes_fnptr_indirect_reason = the function pointer to `{$ty}` is FFI-unsafe due to `{$inner_ty}`
379380
lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention
381+
380382
lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive
381383
lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants
382384

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 74 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use rustc_hir::intravisit::VisitorExt;
1010
use rustc_hir::{self as hir, AmbigArg};
1111
use rustc_middle::bug;
1212
use 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
};
1616
use rustc_session::{declare_lint, declare_lint_pass};
1717
use 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]
174176
fn 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

13381385
impl<'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
}

tests/ui/lint/improper-ctypes/lint-94223.stderr

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ error: `extern` callback uses type `[u8]`, which is not FFI-safe
44
LL | pub fn bad(f: extern "C" fn([u8])) {}
55
| ^^^^^^^^^^^^^^^^^^^ not FFI-safe
66
|
7+
= note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]`
78
= help: consider using a raw pointer instead
89
= note: slices have no C equivalent
910
note: the lint level is defined here
@@ -18,6 +19,7 @@ error: `extern` callback uses type `[u8]`, which is not FFI-safe
1819
LL | pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {}
1920
| ^^^^^^^^^^^^^^^^^^^ not FFI-safe
2021
|
22+
= note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]`
2123
= help: consider using a raw pointer instead
2224
= note: slices have no C equivalent
2325

@@ -27,6 +29,7 @@ error: `extern` callback uses type `[u8]`, which is not FFI-safe
2729
LL | pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {}
2830
| ^^^^^^^^^^^^^^^^^^^ not FFI-safe
2931
|
32+
= note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]`
3033
= help: consider using a raw pointer instead
3134
= note: slices have no C equivalent
3235

@@ -36,6 +39,7 @@ error: `extern` callback uses type `[u8]`, which is not FFI-safe
3639
LL | struct BadStruct(extern "C" fn([u8]));
3740
| ^^^^^^^^^^^^^^^^^^^ not FFI-safe
3841
|
42+
= note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]`
3943
= help: consider using a raw pointer instead
4044
= note: slices have no C equivalent
4145

@@ -45,6 +49,7 @@ error: `extern` callback uses type `[u8]`, which is not FFI-safe
4549
LL | A(extern "C" fn([u8])),
4650
| ^^^^^^^^^^^^^^^^^^^ not FFI-safe
4751
|
52+
= note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]`
4853
= help: consider using a raw pointer instead
4954
= note: slices have no C equivalent
5055

@@ -54,6 +59,7 @@ error: `extern` callback uses type `[u8]`, which is not FFI-safe
5459
LL | A(extern "C" fn([u8])),
5560
| ^^^^^^^^^^^^^^^^^^^ not FFI-safe
5661
|
62+
= note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]`
5763
= help: consider using a raw pointer instead
5864
= note: slices have no C equivalent
5965

@@ -63,6 +69,7 @@ error: `extern` callback uses type `[u8]`, which is not FFI-safe
6369
LL | type Foo = extern "C" fn([u8]);
6470
| ^^^^^^^^^^^^^^^^^^^ not FFI-safe
6571
|
72+
= note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]`
6673
= help: consider using a raw pointer instead
6774
= note: slices have no C equivalent
6875

@@ -72,6 +79,7 @@ error: `extern` callback uses type `Option<&<T as FooTrait>::FooType>`, which is
7279
LL | pub type Foo2<T> = extern "C" fn(Option<&<T as FooTrait>::FooType>);
7380
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
7481
|
82+
= note: the function pointer to `for<'a> extern "C" fn(Option<&'a <T as FooTrait>::FooType>)` is FFI-unsafe due to `Option<&<T as FooTrait>::FooType>`
7583
= help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
7684
= note: enum has no representation hint
7785

@@ -81,6 +89,7 @@ error: `extern` callback uses type `FfiUnsafe`, which is not FFI-safe
8189
LL | pub static BAD: extern "C" fn(FfiUnsafe) = f;
8290
| ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
8391
|
92+
= note: the function pointer to `extern "C" fn(FfiUnsafe)` is FFI-unsafe due to `FfiUnsafe`
8493
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
8594
= note: this struct has unspecified layout
8695
note: the type is defined here
@@ -95,6 +104,7 @@ error: `extern` callback uses type `FfiUnsafe`, which is not FFI-safe
95104
LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUnsafe)> = Ok(f);
96105
| ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
97106
|
107+
= note: the function pointer to `extern "C" fn(FfiUnsafe)` is FFI-unsafe due to `FfiUnsafe`
98108
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
99109
= note: this struct has unspecified layout
100110
note: the type is defined here
@@ -109,6 +119,7 @@ error: `extern` callback uses type `FfiUnsafe`, which is not FFI-safe
109119
LL | pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUnsafe)> = Ok(f);
110120
| ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
111121
|
122+
= note: the function pointer to `extern "C" fn(FfiUnsafe)` is FFI-unsafe due to `FfiUnsafe`
112123
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
113124
= note: this struct has unspecified layout
114125
note: the type is defined here
@@ -123,6 +134,7 @@ error: `extern` callback uses type `FfiUnsafe`, which is not FFI-safe
123134
LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f;
124135
| ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
125136
|
137+
= note: the function pointer to `extern "C" fn(FfiUnsafe)` is FFI-unsafe due to `FfiUnsafe`
126138
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
127139
= note: this struct has unspecified layout
128140
note: the type is defined here

0 commit comments

Comments
 (0)