Skip to content

Commit 5dd54fd

Browse files
committed
lint/ctypes: ext. abi fn-ptr in internal abi fn
Instead of skipping functions with internal ABIs, check that the signature doesn't contain any fn-ptr types with external ABIs that aren't FFI-safe. Signed-off-by: David Wood <david.wood@huawei.com>
1 parent 64165aa commit 5dd54fd

File tree

4 files changed

+81
-17
lines changed

4 files changed

+81
-17
lines changed

compiler/rustc_lint/src/types.rs

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,17 +1230,40 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12301230
}
12311231
}
12321232

1233-
fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
1233+
/// Check if a function's argument types and result type are "ffi-safe".
1234+
///
1235+
/// Argument types and the result type are checked for functions with external ABIs.
1236+
/// For functions with internal ABIs, argument types and the result type are walked to find
1237+
/// fn-ptr types that have external ABIs, as these still need checked.
1238+
fn check_maybe_foreign_fn(&mut self, abi: SpecAbi, def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
12341239
let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
12351240
let sig = self.cx.tcx.erase_late_bound_regions(sig);
12361241

1242+
let is_internal_abi = self.is_internal_abi(abi);
1243+
let check_ty = |this: &mut ImproperCTypesVisitor<'a, 'tcx>,
1244+
span: Span,
1245+
ty: Ty<'tcx>,
1246+
is_return_type: bool| {
1247+
// If this function has an external ABI, then its arguments and return type should be
1248+
// checked..
1249+
if !is_internal_abi {
1250+
this.check_type_for_ffi_and_report_errors(span, ty, false, is_return_type);
1251+
return;
1252+
}
1253+
1254+
// ..but if this function has an internal ABI, then search the argument or return type
1255+
// for any fn-ptr types with external ABI, which should be checked..
1256+
if let Some(fn_ptr_ty) = this.find_fn_ptr_ty_with_external_abi(ty) {
1257+
this.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, is_return_type);
1258+
}
1259+
};
1260+
12371261
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1238-
self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false);
1262+
check_ty(self, input_hir.span, *input_ty, false);
12391263
}
12401264

12411265
if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
1242-
let ret_ty = sig.output();
1243-
self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false, true);
1266+
check_ty(self, ret_hir.span, sig.output(), true);
12441267
}
12451268
}
12461269

@@ -1255,23 +1278,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12551278
SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic
12561279
)
12571280
}
1281+
1282+
/// Find any fn-ptr types with external ABIs in `ty`.
1283+
///
1284+
/// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
1285+
fn find_fn_ptr_ty_with_external_abi(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
1286+
struct FnPtrFinder<'parent, 'a, 'tcx>(&'parent ImproperCTypesVisitor<'a, 'tcx>);
1287+
impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'vis, 'a, 'tcx> {
1288+
type BreakTy = Ty<'tcx>;
1289+
1290+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
1291+
if let ty::FnPtr(sig) = ty.kind() && !self.0.is_internal_abi(sig.abi()) {
1292+
ControlFlow::Break(ty)
1293+
} else {
1294+
ty.super_visit_with(self)
1295+
}
1296+
}
1297+
}
1298+
1299+
self.cx
1300+
.tcx
1301+
.normalize_erasing_regions(self.cx.param_env, ty)
1302+
.visit_with(&mut FnPtrFinder(&*self))
1303+
.break_value()
1304+
}
12581305
}
12591306

12601307
impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
12611308
fn check_foreign_item(&mut self, cx: &LateContext<'_>, it: &hir::ForeignItem<'_>) {
12621309
let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
12631310
let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
12641311

1265-
if !vis.is_internal_abi(abi) {
1266-
match it.kind {
1267-
hir::ForeignItemKind::Fn(ref decl, _, _) => {
1268-
vis.check_foreign_fn(it.owner_id.def_id, decl);
1269-
}
1270-
hir::ForeignItemKind::Static(ref ty, _) => {
1271-
vis.check_foreign_static(it.owner_id, ty.span);
1272-
}
1273-
hir::ForeignItemKind::Type => (),
1312+
match it.kind {
1313+
hir::ForeignItemKind::Fn(ref decl, _, _) => {
1314+
vis.check_maybe_foreign_fn(abi, it.owner_id.def_id, decl);
1315+
}
1316+
hir::ForeignItemKind::Static(ref ty, _) if !vis.is_internal_abi(abi) => {
1317+
vis.check_foreign_static(it.owner_id, ty.span);
12741318
}
1319+
hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
12751320
}
12761321
}
12771322
}
@@ -1295,9 +1340,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
12951340
};
12961341

12971342
let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1298-
if !vis.is_internal_abi(abi) {
1299-
vis.check_foreign_fn(id, decl);
1300-
}
1343+
vis.check_maybe_foreign_fn(abi, id, decl);
13011344
}
13021345
}
13031346

tests/ui/abi/foreign/foreign-fn-with-byval.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// run-pass
2-
#![allow(improper_ctypes)]
2+
#![allow(improper_ctypes, improper_ctypes_definitions)]
33

44
// ignore-wasm32-bare no libc to test ffi with
55

tests/ui/lint/lint-ctypes-94223.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![crate_type = "lib"]
2+
#![deny(improper_ctypes_definitions)]
3+
4+
pub fn bad(f: extern "C" fn([u8])) {}
5+
//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: `extern` fn uses type `[u8]`, which is not FFI-safe
2+
--> $DIR/lint-ctypes-94223.rs:4:15
3+
|
4+
LL | pub fn bad(f: extern "C" fn([u8])) {}
5+
| ^^^^^^^^^^^^^^^^^^^ not FFI-safe
6+
|
7+
= help: consider using a raw pointer instead
8+
= note: slices have no C equivalent
9+
note: the lint level is defined here
10+
--> $DIR/lint-ctypes-94223.rs:2:9
11+
|
12+
LL | #![deny(improper_ctypes_definitions)]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: aborting due to previous error
16+

0 commit comments

Comments
 (0)