Skip to content

Commit 8bc7d92

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 f9989d0 commit 8bc7d92

File tree

5 files changed

+108
-71
lines changed

5 files changed

+108
-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: 76 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;
@@ -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]
175177
fn 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
}

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)