From 4f096f4ee03e0211c776f918f4b5bf895f43cf25 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 4 Jun 2019 22:59:59 +0200 Subject: [PATCH 1/3] Allow closure to extern fn pointer coercion --- src/librustc/ty/adjustment.rs | 3 ++- src/librustc/ty/context.rs | 15 +++++++-------- src/librustc_codegen_ssa/mir/rvalue.rs | 2 +- .../borrow_check/nll/type_check/mod.rs | 4 ++-- src/librustc_mir/interpret/cast.rs | 2 +- src/librustc_mir/monomorphize/collector.rs | 2 +- .../transform/qualify_min_const_fn.rs | 2 +- src/librustc_typeck/check/coercion.rs | 5 +++-- .../coerce-unsafe-closure-to-unsafe-fn-ptr.rs | 3 +++ .../run-pass/typeck-closure-to-extern-c-fn-ptr.rs | 7 +++++++ .../typeck-closure-to-unsafe-extern-c-fn-ptr.rs | 7 +++++++ 11 files changed, 35 insertions(+), 17 deletions(-) create mode 100644 src/test/run-pass/typeck-closure-to-extern-c-fn-ptr.rs create mode 100644 src/test/run-pass/typeck-closure-to-unsafe-extern-c-fn-ptr.rs diff --git a/src/librustc/ty/adjustment.rs b/src/librustc/ty/adjustment.rs index 11aad87b70dd3..4ed97d64d9cf9 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc/ty/adjustment.rs @@ -3,6 +3,7 @@ use crate::hir::def_id::DefId; use crate::ty::{self, Ty, TyCtxt}; use crate::ty::subst::SubstsRef; use rustc_macros::HashStable; +use rustc_target::spec::abi; #[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] @@ -15,7 +16,7 @@ pub enum PointerCast { /// Go from a non-capturing closure to an fn pointer or an unsafe fn pointer. /// It cannot convert a closure that requires unsafe. - ClosureFnPointer(hir::Unsafety), + ClosureFnPointer(hir::Unsafety, abi::Abi), /// Go from a mut raw pointer to a const raw pointer. MutToConstPointer, diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index e60022033cc54..f4f90c109280c 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2420,7 +2420,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// `hir::Unsafety::Unsafe` in the previous example, then you would get /// an `unsafe fn (u32, i32)`. /// It cannot convert a closure that requires unsafe. - pub fn coerce_closure_fn_ty(self, sig: PolyFnSig<'tcx>, unsafety: hir::Unsafety) -> Ty<'tcx> { + pub fn coerce_closure_fn_ty( + self, + sig: PolyFnSig<'tcx>, + unsafety: hir::Unsafety, + abi: abi::Abi, + ) -> Ty<'tcx> { let converted_sig = sig.map_bound(|s| { let params_iter = match s.inputs()[0].sty { ty::Tuple(params) => { @@ -2428,13 +2433,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } _ => bug!(), }; - self.mk_fn_sig( - params_iter, - s.output(), - s.c_variadic, - unsafety, - abi::Abi::Rust, - ) + self.mk_fn_sig(params_iter, s.output(), s.c_variadic, unsafety, abi) }); self.mk_fn_ptr(converted_sig) diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 7a2bd18df4ec9..1764d71df71d8 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -192,7 +192,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } } - mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)) => { + mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_, _)) => { match operand.layout.ty.sty { ty::Closure(def_id, substs) => { let instance = Instance::resolve_closure( diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index d6da42c24cee4..7c3a8b24667fe 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -2011,14 +2011,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => { + CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety, abi)) => { let sig = match op.ty(body, tcx).sty { ty::Closure(def_id, substs) => { substs.closure_sig_ty(def_id, tcx).fn_sig(tcx) } _ => bug!(), }; - let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig, *unsafety); + let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig, *unsafety, *abi); if let Err(terr) = self.eq_types( ty_fn_ptr_from, diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 6392e0996aec2..ac5e0a928620d 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -104,7 +104,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> } } - Pointer(PointerCast::ClosureFnPointer(_)) => { + Pointer(PointerCast::ClosureFnPointer(_, _)) => { // The src operand does not matter, just its type match src.layout.ty.sty { ty::Closure(def_id, substs) => { diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index f084919ac057c..b74a034e56a8f 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -569,7 +569,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { visit_fn_use(self.tcx, fn_ty, false, &mut self.output); } mir::Rvalue::Cast( - mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)), ref operand, _ + mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_, _)), ref operand, _ ) => { let source_ty = operand.ty(self.body, self.tcx); let source_ty = self.tcx.subst_and_normalize_erasing_regions( diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index f96675864562f..ad22e1b0049d2 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -160,7 +160,7 @@ fn check_rvalue( check_operand(operand, span) } Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) | - Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) | + Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_, _)), _, _) | Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) => Err(( span, "function pointer casts are not allowed in const fn".into(), diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index f95021f0cb088..28d592f777fd6 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -739,11 +739,12 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { // `unsafe fn(arg0,arg1,...) -> _` let sig = self.closure_sig(def_id_a, substs_a); let unsafety = fn_ty.unsafety(); - let pointer_ty = self.tcx.coerce_closure_fn_ty(sig, unsafety); + let abi = fn_ty.abi(); + let pointer_ty = self.tcx.coerce_closure_fn_ty(sig, unsafety, abi); debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); self.unify_and(pointer_ty, b, simple( - Adjust::Pointer(PointerCast::ClosureFnPointer(unsafety)) + Adjust::Pointer(PointerCast::ClosureFnPointer(unsafety, abi)) )) } _ => self.unify_and(a, b, identity), diff --git a/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs b/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs index 36777693faba0..f7345826da8cf 100644 --- a/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs +++ b/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs @@ -1,5 +1,8 @@ fn main() { let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; //~^ ERROR E0133 + let _: unsafe extern "C" fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; + //~^ ERROR E0133 let _: unsafe fn() = || unsafe { ::std::pin::Pin::new_unchecked(&0_u8); }; // OK + let _: unsafe extern "C" fn() = || unsafe { ::std::pin::Pin::new_unchecked(&0_u8); }; // OK } diff --git a/src/test/run-pass/typeck-closure-to-extern-c-fn-ptr.rs b/src/test/run-pass/typeck-closure-to-extern-c-fn-ptr.rs new file mode 100644 index 0000000000000..277ff7adaeedb --- /dev/null +++ b/src/test/run-pass/typeck-closure-to-extern-c-fn-ptr.rs @@ -0,0 +1,7 @@ +fn call_extern_c(func: extern "C" fn()) { + func() +} + +pub fn main() { + call_extern_c(|| {}); +} diff --git a/src/test/run-pass/typeck-closure-to-unsafe-extern-c-fn-ptr.rs b/src/test/run-pass/typeck-closure-to-unsafe-extern-c-fn-ptr.rs new file mode 100644 index 0000000000000..f252e45389e68 --- /dev/null +++ b/src/test/run-pass/typeck-closure-to-unsafe-extern-c-fn-ptr.rs @@ -0,0 +1,7 @@ +unsafe fn call_unsafe_extern_c(func: unsafe extern "C" fn()) { + func() +} + +pub fn main() { + unsafe { call_unsafe_extern_c(|| {}); } +} From 117c8b17d7c3b13b940998a8c5bb44478adef45b Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 12 Jun 2019 00:45:24 +0200 Subject: [PATCH 2/3] Move closure-to-extern-fn-ptr-coercion tests into ui --- .../coerce-unsafe-closure-to-unsafe-fn-ptr.rs | 3 --- .../typeck-closure-to-extern-c-fn-ptr.rs | 7 ------- .../typeck-closure-to-unsafe-extern-c-fn-ptr.rs | 7 ------- .../coerce-closure-to-extern-c-fn-ptr.rs | 16 ++++++++++++++++ ...rce-unsafe-closure-to-unsafe-extern-fn-ptr.rs | 4 ++++ ...unsafe-closure-to-unsafe-extern-fn-ptr.stderr | 11 +++++++++++ 6 files changed, 31 insertions(+), 17 deletions(-) delete mode 100644 src/test/run-pass/typeck-closure-to-extern-c-fn-ptr.rs delete mode 100644 src/test/run-pass/typeck-closure-to-unsafe-extern-c-fn-ptr.rs create mode 100644 src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-to-extern-c-fn-ptr.rs create mode 100644 src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs create mode 100644 src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.stderr diff --git a/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs b/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs index f7345826da8cf..36777693faba0 100644 --- a/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs +++ b/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs @@ -1,8 +1,5 @@ fn main() { let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; //~^ ERROR E0133 - let _: unsafe extern "C" fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; - //~^ ERROR E0133 let _: unsafe fn() = || unsafe { ::std::pin::Pin::new_unchecked(&0_u8); }; // OK - let _: unsafe extern "C" fn() = || unsafe { ::std::pin::Pin::new_unchecked(&0_u8); }; // OK } diff --git a/src/test/run-pass/typeck-closure-to-extern-c-fn-ptr.rs b/src/test/run-pass/typeck-closure-to-extern-c-fn-ptr.rs deleted file mode 100644 index 277ff7adaeedb..0000000000000 --- a/src/test/run-pass/typeck-closure-to-extern-c-fn-ptr.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn call_extern_c(func: extern "C" fn()) { - func() -} - -pub fn main() { - call_extern_c(|| {}); -} diff --git a/src/test/run-pass/typeck-closure-to-unsafe-extern-c-fn-ptr.rs b/src/test/run-pass/typeck-closure-to-unsafe-extern-c-fn-ptr.rs deleted file mode 100644 index f252e45389e68..0000000000000 --- a/src/test/run-pass/typeck-closure-to-unsafe-extern-c-fn-ptr.rs +++ /dev/null @@ -1,7 +0,0 @@ -unsafe fn call_unsafe_extern_c(func: unsafe extern "C" fn()) { - func() -} - -pub fn main() { - unsafe { call_unsafe_extern_c(|| {}); } -} diff --git a/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-to-extern-c-fn-ptr.rs b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-to-extern-c-fn-ptr.rs new file mode 100644 index 0000000000000..1ccaf0131ad49 --- /dev/null +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-to-extern-c-fn-ptr.rs @@ -0,0 +1,16 @@ +// compile-pass + +fn call_extern_c(func: extern "C" fn()) { + func() +} + +unsafe fn call_unsafe_extern_c(func: unsafe extern "C" fn()) { + func() +} + +pub fn main() { + call_extern_c(|| {}); + unsafe { + call_unsafe_extern_c(|| {}); + } +} diff --git a/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs new file mode 100644 index 0000000000000..f54e7cdddc696 --- /dev/null +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs @@ -0,0 +1,4 @@ +fn main() { + let _: unsafe extern "C" fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; //~ ERROR E0133 + let _: unsafe extern "C" fn() = || unsafe { ::std::pin::Pin::new_unchecked(&0_u8); }; // OK +} diff --git a/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.stderr b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.stderr new file mode 100644 index 0000000000000..05791ca9287ad --- /dev/null +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.stderr @@ -0,0 +1,11 @@ +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs:2:42 + | +LL | let _: unsafe extern "C" fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. From f1716a6ff941f1d17fdb15b678b3e7b5dbf7ffa3 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sat, 22 Jun 2019 14:00:22 +0200 Subject: [PATCH 3/3] closure_to_fn_coercion: Define feature gate and add a corresponding test. It still fails because the feature gate is not actually implemented. [ci skip] --- src/libsyntax/feature_gate.rs | 3 +++ src/libsyntax_pos/symbol.rs | 1 + ...tern-c-fn-ptr.rs => coerce-closure-extern-c.rs} | 2 ++ ...fn-ptr.rs => coerce-closure-unsafe-extern-c.rs} | 2 ++ ...tderr => coerce-closure-unsafe-extern-c.stderr} | 2 +- .../feature-gate-required.rs | 14 ++++++++++++++ 6 files changed, 23 insertions(+), 1 deletion(-) rename src/test/ui/closure-to-extern-fn-ptr-coercion/{coerce-closure-to-extern-c-fn-ptr.rs => coerce-closure-extern-c.rs} (85%) rename src/test/ui/closure-to-extern-fn-ptr-coercion/{coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs => coerce-closure-unsafe-extern-c.rs} (82%) rename src/test/ui/closure-to-extern-fn-ptr-coercion/{coerce-unsafe-closure-to-unsafe-extern-fn-ptr.stderr => coerce-closure-unsafe-extern-c.stderr} (88%) create mode 100644 src/test/ui/closure-to-extern-fn-ptr-coercion/feature-gate-required.rs diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 044c4b18905ef..5c3b4d0bb6b46 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -567,6 +567,9 @@ declare_features! ( // #[repr(transparent)] on unions. (active, transparent_unions, "1.37.0", Some(60405), None), + // Allows coercing non-capturing closures to fn pointers of non-Rust ABI + (active, closure_to_extern_fn_coercion, "1.37.0", Some(44291), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 875c286bd8a90..52a0bf93aacc7 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -175,6 +175,7 @@ symbols! { Clone, clone_closures, clone_from, + closure_to_extern_fn_coercion, closure_to_fn_coercion, cmp, cmpxchg16b_target_feature, diff --git a/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-to-extern-c-fn-ptr.rs b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-extern-c.rs similarity index 85% rename from src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-to-extern-c-fn-ptr.rs rename to src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-extern-c.rs index 1ccaf0131ad49..810b6e012ee45 100644 --- a/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-to-extern-c-fn-ptr.rs +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-extern-c.rs @@ -1,5 +1,7 @@ // compile-pass +#![feature(closure_to_extern_fn_coercion)] + fn call_extern_c(func: extern "C" fn()) { func() } diff --git a/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.rs similarity index 82% rename from src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs rename to src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.rs index f54e7cdddc696..192a9732c2e2b 100644 --- a/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.rs @@ -1,3 +1,5 @@ +#![feature(closure_to_extern_fn_coercion)] + fn main() { let _: unsafe extern "C" fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; //~ ERROR E0133 let _: unsafe extern "C" fn() = || unsafe { ::std::pin::Pin::new_unchecked(&0_u8); }; // OK diff --git a/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.stderr b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.stderr similarity index 88% rename from src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.stderr rename to src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.stderr index 05791ca9287ad..c256be35a9edf 100644 --- a/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.stderr +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.stderr @@ -1,5 +1,5 @@ error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/coerce-unsafe-closure-to-unsafe-extern-fn-ptr.rs:2:42 + --> $DIR/coerce-closure-unsafe-extern-c.rs:4:42 | LL | let _: unsafe extern "C" fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function diff --git a/src/test/ui/closure-to-extern-fn-ptr-coercion/feature-gate-required.rs b/src/test/ui/closure-to-extern-fn-ptr-coercion/feature-gate-required.rs new file mode 100644 index 0000000000000..f42ce2da6c42a --- /dev/null +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/feature-gate-required.rs @@ -0,0 +1,14 @@ +fn call_extern_c(func: extern "C" fn()) { + func() +} + +unsafe fn call_unsafe_extern_c(func: unsafe extern "C" fn()) { + func() +} + +pub fn main() { + call_extern_c(|| {}); //~ ERROR + unsafe { + call_unsafe_extern_c(|| {}); //~ ERROR + } +}