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/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-extern-c.rs b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-extern-c.rs new file mode 100644 index 0000000000000..810b6e012ee45 --- /dev/null +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-extern-c.rs @@ -0,0 +1,18 @@ +// compile-pass + +#![feature(closure_to_extern_fn_coercion)] + +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-closure-unsafe-extern-c.rs b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.rs new file mode 100644 index 0000000000000..192a9732c2e2b --- /dev/null +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.rs @@ -0,0 +1,6 @@ +#![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-closure-unsafe-extern-c.stderr b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.stderr new file mode 100644 index 0000000000000..c256be35a9edf --- /dev/null +++ b/src/test/ui/closure-to-extern-fn-ptr-coercion/coerce-closure-unsafe-extern-c.stderr @@ -0,0 +1,11 @@ +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $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 + | + = 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`. 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 + } +}