Skip to content

Commit 3d4ce20

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 a941112 commit 3d4ce20

File tree

5 files changed

+105
-71
lines changed

5 files changed

+105
-71
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,9 @@ lint_improper_ctypes_enum_repr_help =
383383
lint_improper_ctypes_enum_repr_reason = enum has no representation hint
384384
lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead
385385
386+
lint_improper_ctypes_fnptr_indirect_reason = the function pointer to `{$ty}` is FFI-unsafe due to `{$inner_ty}`
386387
lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention
388+
387389
lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive
388390
lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants
389391

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use rustc_hir::intravisit::VisitorExt;
1111
use rustc_hir::{self as hir, AmbigArg};
1212
use rustc_middle::bug;
1313
use 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
};
1717
use rustc_session::{declare_lint, declare_lint_pass};
1818
use rustc_span::def_id::LocalDefId;
@@ -23,6 +23,8 @@ use super::repr_nullable_ptr;
2323
use crate::lints::{ImproperCTypes, ImproperCTypesLayer, UsesPowerAlignment};
2424
use 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]
2830
fn 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
}

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

tests/ui/lint/improper_ctypes/lint-fn.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![allow(private_interfaces)]
2-
#![deny(improper_c_fn_definitions, improper_c_callbacks)]
2+
#![deny(improper_c_callbacks, improper_c_fn_definitions)]
33

44
use std::default::Default;
55
use std::marker::PhantomData;
@@ -114,21 +114,22 @@ pub extern "C" fn fn_type2(p: fn()) { }
114114
//~^ ERROR uses type `fn()`
115115

116116
pub extern "C" fn fn_contained(p: RustBadRet) { }
117-
//~^ ERROR: uses type `(u32, u64)`
117+
// ^ FIXME it doesn't see the error... but at least it reports it elsewhere?
118118

119119
pub extern "C" fn transparent_str(p: TransparentStr) { }
120120
//~^ ERROR: uses type `&str`
121121

122122
pub extern "C" fn transparent_fn(p: TransparentBadFn) { }
123-
//~^ ERROR: uses type `(u32, u64)`
123+
// ^ possible FIXME: it doesn't see the actual FnPtr's error...
124+
// but at least it reports it elsewhere?
124125

125126
pub extern "C" fn good3(fptr: Option<extern "C" fn()>) { }
126127

127-
pub extern "C" fn good4(aptr: &[u8; 4 as usize]) { }
128+
pub extern "C" fn argument_with_assumptions_4(aptr: &[u8; 4 as usize]) { }
128129

129130
pub extern "C" fn good5(s: StructWithProjection) { }
130131

131-
pub extern "C" fn good6(s: StructWithProjectionAndLifetime) { }
132+
pub extern "C" fn argument_with_assumptions_6(s: StructWithProjectionAndLifetime) { }
132133

133134
pub extern "C" fn good7(fptr: extern "C" fn() -> ()) { }
134135

@@ -144,7 +145,7 @@ pub extern "C" fn good12(size: usize) { }
144145

145146
pub extern "C" fn good13(n: TransparentInt) { }
146147

147-
pub extern "C" fn good14(p: TransparentRef) { }
148+
pub extern "C" fn argument_with_assumptions_14(p: TransparentRef) { }
148149

149150
pub extern "C" fn good15(p: TransparentLifetime) { }
150151

0 commit comments

Comments
 (0)