Skip to content

Commit 3f0e164

Browse files
committed
Auto merge of #65134 - davidtwco:issue-19834-improper-ctypes-in-extern-C-fn, r=rkruppe
improper_ctypes: `extern "C"` fns cc #19834. Fixes #65867. This pull request implements the change [described in this comment](#19834 (comment)). cc @rkruppe @varkor @shepmaster
2 parents 61a551b + 49e2403 commit 3f0e164

18 files changed

+521
-37
lines changed

src/libpanic_abort/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
// Rust's "try" function, but if we're aborting on panics we just call the
2222
// function as there's nothing else we need to do here.
2323
#[rustc_std_internal_symbol]
24+
#[allow(improper_ctypes)]
2425
pub unsafe extern fn __rust_maybe_catch_panic(f: fn(*mut u8),
2526
data: *mut u8,
2627
_data_ptr: *mut usize,

src/libpanic_unwind/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ mod dwarf;
6969
// hairy and tightly coupled, for more information see the compiler's
7070
// implementation of this.
7171
#[no_mangle]
72+
#[allow(improper_ctypes)]
7273
pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
7374
data: *mut u8,
7475
data_ptr: *mut usize,

src/librustc_codegen_llvm/back/write.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,7 @@ impl<'a> Drop for DiagnosticHandlers<'a> {
240240
}
241241
}
242242

243-
unsafe extern "C" fn report_inline_asm(cgcx: &CodegenContext<LlvmCodegenBackend>,
244-
msg: &str,
245-
cookie: c_uint) {
243+
fn report_inline_asm(cgcx: &CodegenContext<LlvmCodegenBackend>, msg: &str, cookie: c_uint) {
246244
cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_owned());
247245
}
248246

src/librustc_codegen_llvm/llvm/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ pub struct RustString {
8787
}
8888

8989
/// Appending to a Rust string -- used by RawRustStringOstream.
90+
#[allow(improper_ctypes)]
9091
#[no_mangle]
9192
pub unsafe extern "C" fn LLVMRustStringWriteImpl(sr: &RustString,
9293
ptr: *const c_char,

src/librustc_lint/types.rs

+69-33
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc::hir::{ExprKind, Node};
44
use crate::hir::def_id::DefId;
55
use rustc::hir::lowering::is_range_literal;
66
use rustc::ty::subst::SubstsRef;
7-
use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
7+
use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt, TypeFoldable};
88
use rustc::ty::layout::{self, IntegerExt, LayoutOf, VariantIdx, SizeSkeleton};
99
use rustc::{lint, util};
1010
use rustc_index::vec::Idx;
@@ -835,16 +835,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
835835
ty::Array(ty, _) => self.check_type_for_ffi(cache, ty),
836836

837837
ty::FnPtr(sig) => {
838-
match sig.abi() {
839-
Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
840-
return FfiUnsafe {
841-
ty,
842-
reason: "this function pointer has Rust-specific calling convention",
843-
help: Some("consider using an `extern fn(...) -> ...` \
844-
function pointer instead"),
845-
}
846-
}
847-
_ => {}
838+
if self.is_internal_abi(sig.abi()) {
839+
return FfiUnsafe {
840+
ty,
841+
reason: "this function pointer has Rust-specific calling convention",
842+
help: Some("consider using an `extern fn(...) -> ...` \
843+
function pointer instead"),
844+
};
848845
}
849846

850847
let sig = cx.erase_late_bound_regions(&sig);
@@ -871,7 +868,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
871868

872869
ty::Foreign(..) => FfiSafe,
873870

874-
ty::Param(..) |
871+
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
872+
// so they are currently ignored for the purposes of this lint, see #65134.
873+
ty::Param(..) | ty::Projection(..) => FfiSafe,
874+
875875
ty::Infer(..) |
876876
ty::Bound(..) |
877877
ty::Error |
@@ -880,7 +880,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
880880
ty::GeneratorWitness(..) |
881881
ty::Placeholder(..) |
882882
ty::UnnormalizedProjection(..) |
883-
ty::Projection(..) |
884883
ty::Opaque(..) |
885884
ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
886885
}
@@ -892,11 +891,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
892891
sp: Span,
893892
note: &str,
894893
help: Option<&str>,
894+
is_foreign_item: bool,
895895
) {
896896
let mut diag = self.cx.struct_span_lint(
897897
IMPROPER_CTYPES,
898898
sp,
899-
&format!("`extern` block uses type `{}`, which is not FFI-safe", ty),
899+
&format!(
900+
"`extern` {} uses type `{}`, which is not FFI-safe",
901+
if is_foreign_item { "block" } else { "fn" },
902+
ty,
903+
),
900904
);
901905
diag.span_label(sp, "not FFI-safe");
902906
if let Some(help) = help {
@@ -911,9 +915,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
911915
diag.emit();
912916
}
913917

914-
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
915-
use crate::rustc::ty::TypeFoldable;
916-
918+
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>, is_foreign_item: bool) -> bool {
917919
struct ProhibitOpaqueTypes<'tcx> {
918920
ty: Option<Ty<'tcx>>,
919921
};
@@ -937,70 +939,81 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
937939
sp,
938940
"opaque types have no C equivalent",
939941
None,
942+
is_foreign_item,
940943
);
941944
true
942945
} else {
943946
false
944947
}
945948
}
946949

947-
fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) {
950+
fn check_type_for_ffi_and_report_errors(
951+
&mut self,
952+
sp: Span,
953+
ty: Ty<'tcx>,
954+
is_foreign_item: bool,
955+
) {
948956
// We have to check for opaque types before `normalize_erasing_regions`,
949957
// which will replace opaque types with their underlying concrete type.
950-
if self.check_for_opaque_ty(sp, ty) {
958+
if self.check_for_opaque_ty(sp, ty, is_foreign_item) {
951959
// We've already emitted an error due to an opaque type.
952960
return;
953961
}
954962

955-
// it is only OK to use this function because extern fns cannot have
956-
// any generic types right now:
957-
let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
958-
963+
let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty);
959964
match self.check_type_for_ffi(&mut FxHashSet::default(), ty) {
960965
FfiResult::FfiSafe => {}
961966
FfiResult::FfiPhantom(ty) => {
962-
self.emit_ffi_unsafe_type_lint(ty, sp, "composed only of `PhantomData`", None);
967+
self.emit_ffi_unsafe_type_lint(
968+
ty, sp, "composed only of `PhantomData`", None, is_foreign_item);
963969
}
964970
FfiResult::FfiUnsafe { ty, reason, help } => {
965-
self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
971+
self.emit_ffi_unsafe_type_lint(
972+
ty, sp, reason, help, is_foreign_item);
966973
}
967974
}
968975
}
969976

970-
fn check_foreign_fn(&mut self, id: hir::HirId, decl: &hir::FnDecl) {
977+
fn check_foreign_fn(&mut self, id: hir::HirId, decl: &hir::FnDecl, is_foreign_item: bool) {
971978
let def_id = self.cx.tcx.hir().local_def_id(id);
972979
let sig = self.cx.tcx.fn_sig(def_id);
973980
let sig = self.cx.tcx.erase_late_bound_regions(&sig);
974981

975982
for (input_ty, input_hir) in sig.inputs().iter().zip(&decl.inputs) {
976-
self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty);
983+
self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty, is_foreign_item);
977984
}
978985

979986
if let hir::Return(ref ret_hir) = decl.output {
980987
let ret_ty = sig.output();
981988
if !ret_ty.is_unit() {
982-
self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty);
989+
self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, is_foreign_item);
983990
}
984991
}
985992
}
986993

987994
fn check_foreign_static(&mut self, id: hir::HirId, span: Span) {
988995
let def_id = self.cx.tcx.hir().local_def_id(id);
989996
let ty = self.cx.tcx.type_of(def_id);
990-
self.check_type_for_ffi_and_report_errors(span, ty);
997+
self.check_type_for_ffi_and_report_errors(span, ty, true);
998+
}
999+
1000+
fn is_internal_abi(&self, abi: Abi) -> bool {
1001+
if let Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
1002+
true
1003+
} else {
1004+
false
1005+
}
9911006
}
9921007
}
9931008

9941009
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes {
9951010
fn check_foreign_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::ForeignItem) {
9961011
let mut vis = ImproperCTypesVisitor { cx };
9971012
let abi = cx.tcx.hir().get_foreign_abi(it.hir_id);
998-
if let Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
999-
// Don't worry about types in internal ABIs.
1000-
} else {
1013+
if !vis.is_internal_abi(abi) {
10011014
match it.kind {
10021015
hir::ForeignItemKind::Fn(ref decl, _, _) => {
1003-
vis.check_foreign_fn(it.hir_id, decl);
1016+
vis.check_foreign_fn(it.hir_id, decl, true);
10041017
}
10051018
hir::ForeignItemKind::Static(ref ty, _) => {
10061019
vis.check_foreign_static(it.hir_id, ty.span);
@@ -1009,6 +1022,29 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes {
10091022
}
10101023
}
10111024
}
1025+
1026+
fn check_fn(
1027+
&mut self,
1028+
cx: &LateContext<'a, 'tcx>,
1029+
kind: hir::intravisit::FnKind<'tcx>,
1030+
decl: &'tcx hir::FnDecl,
1031+
_: &'tcx hir::Body,
1032+
_: Span,
1033+
hir_id: hir::HirId,
1034+
) {
1035+
use hir::intravisit::FnKind;
1036+
1037+
let abi = match kind {
1038+
FnKind::ItemFn(_, _, header, ..) => (header.abi),
1039+
FnKind::Method(_, sig, ..) => (sig.header.abi),
1040+
_ => return,
1041+
};
1042+
1043+
let mut vis = ImproperCTypesVisitor { cx };
1044+
if !vis.is_internal_abi(abi) {
1045+
vis.check_foreign_fn(hir_id, decl, false);
1046+
}
1047+
}
10121048
}
10131049

10141050
declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);

src/libstd/sys/sgx/abi/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) {
5353
// (main function exists). If this is a library, the crate author should be
5454
// able to specify this
5555
#[cfg(not(test))]
56+
#[allow(improper_ctypes)]
5657
#[no_mangle]
5758
extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) {
5859
// FIXME: how to support TLS in library mode?

src/libstd/sys/sgx/rwlock.rs

+3
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ const EINVAL: i32 = 22;
172172

173173
#[cfg(not(test))]
174174
#[no_mangle]
175+
#[allow(improper_ctypes)]
175176
pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
176177
if p.is_null() {
177178
return EINVAL;
@@ -181,6 +182,7 @@ pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
181182
}
182183

183184
#[cfg(not(test))]
185+
#[allow(improper_ctypes)]
184186
#[no_mangle]
185187
pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
186188
if p.is_null() {
@@ -190,6 +192,7 @@ pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
190192
return 0;
191193
}
192194
#[cfg(not(test))]
195+
#[allow(improper_ctypes)]
193196
#[no_mangle]
194197
pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
195198
if p.is_null() {

src/test/ui/abi/abi-sysv64-register-usage.rs

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub extern "sysv64" fn all_the_registers(rdi: i64, rsi: i64, rdx: i64,
3333

3434
// this struct contains 8 i64's, while only 6 can be passed in registers.
3535
#[cfg(target_arch = "x86_64")]
36+
#[repr(C)]
3637
#[derive(PartialEq, Eq, Debug)]
3738
pub struct LargeStruct(i64, i64, i64, i64, i64, i64, i64, i64);
3839

src/test/ui/align-with-extern-c-fn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
#![feature(repr_align)]
99

10-
#[repr(align(16))]
10+
#[repr(align(16), C)]
1111
pub struct A(i64);
1212

1313
pub extern "C" fn foo(x: A) {}

src/test/ui/issues/issue-16441.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
struct Empty;
66

77
// This used to cause an ICE
8+
#[allow(improper_ctypes)]
89
extern "C" fn ice(_a: Empty) {}
910

1011
fn main() {

src/test/ui/issues/issue-26997.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub struct Foo {
66
}
77

88
impl Foo {
9+
#[allow(improper_ctypes)]
910
pub extern fn foo_new() -> Foo {
1011
Foo { x: 21, y: 33 }
1112
}

src/test/ui/issues/issue-28600.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
struct Test;
55

66
impl Test {
7+
#[allow(improper_ctypes)]
78
#[allow(dead_code)]
89
#[allow(unused_variables)]
910
pub extern fn test(val: &str) {

src/test/ui/issues/issue-38763.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#[repr(C)]
55
pub struct Foo(i128);
66

7+
#[allow(improper_ctypes)]
78
#[no_mangle]
89
pub extern "C" fn foo(x: Foo) -> Foo { x }
910

src/test/ui/issues/issue-51907.rs

+2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ trait Foo {
66

77
struct Bar;
88
impl Foo for Bar {
9+
#[allow(improper_ctypes)]
910
extern fn borrow(&self) {}
11+
#[allow(improper_ctypes)]
1012
extern fn take(self: Box<Self>) {}
1113
}
1214

0 commit comments

Comments
 (0)