diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 01e57273e54a1..4f630fe9a3911 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -707,7 +707,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { adjustment::Adjust::NeverToAny | adjustment::Adjust::ReifyFnPointer | adjustment::Adjust::UnsafeFnPointer | - adjustment::Adjust::ClosureFnPointer | + adjustment::Adjust::ClosureFnPointer(_) | adjustment::Adjust::MutToConstPointer | adjustment::Adjust::Unsize => { // Creating a closure/fn-pointer or unsizing consumes diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 4b169dea06c7c..1a3fef18404e3 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -621,7 +621,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { adjustment::Adjust::NeverToAny | adjustment::Adjust::ReifyFnPointer | adjustment::Adjust::UnsafeFnPointer | - adjustment::Adjust::ClosureFnPointer | + adjustment::Adjust::ClosureFnPointer(_) | adjustment::Adjust::MutToConstPointer | adjustment::Adjust::Borrow(_) | adjustment::Adjust::Unsize => { diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index d35ee1e57d5ce..7b419e306db60 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2247,8 +2247,9 @@ pub enum CastKind { /// Converts unique, zero-sized type for a fn to fn() ReifyFnPointer, - /// Converts non capturing closure to fn() - ClosureFnPointer, + /// Converts non capturing closure to fn() or unsafe fn(). + /// It cannot convert a closure that requires unsafe. + ClosureFnPointer(hir::Unsafety), /// Converts safe fn() to unsafe fn() UnsafeFnPointer, diff --git a/src/librustc/ty/adjustment.rs b/src/librustc/ty/adjustment.rs index f9149ce0f6e6b..c2ef08c4c40fe 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc/ty/adjustment.rs @@ -62,8 +62,9 @@ pub enum Adjust<'tcx> { /// Go from a safe fn pointer to an unsafe fn pointer. UnsafeFnPointer, - /// Go from a non-capturing closure to an fn pointer. - ClosureFnPointer, + /// 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), /// 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 ea003ba1ac701..77330c7a9d1b2 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2441,7 +2441,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// type with the same signature. Detuples and so forth -- so /// e.g., if we have a sig with `Fn<(u32, i32)>` then you would get /// a `fn(u32, i32)`. - pub fn coerce_closure_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> { + /// `unsafety` determines the unsafety of the `fn` type. If you pass + /// `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> { let converted_sig = sig.map_bound(|s| { let params_iter = match s.inputs()[0].sty { ty::Tuple(params) => { @@ -2453,7 +2457,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { params_iter, s.output(), s.c_variadic, - hir::Unsafety::Normal, + unsafety, abi::Abi::Rust, ) }); diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index cbdda73269282..4f1fda3f4e534 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -630,8 +630,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> { Some(ty::adjustment::Adjust::ReifyFnPointer), ty::adjustment::Adjust::UnsafeFnPointer => Some(ty::adjustment::Adjust::UnsafeFnPointer), - ty::adjustment::Adjust::ClosureFnPointer => - Some(ty::adjustment::Adjust::ClosureFnPointer), + ty::adjustment::Adjust::ClosureFnPointer(unsafety) => + Some(ty::adjustment::Adjust::ClosureFnPointer(unsafety)), ty::adjustment::Adjust::MutToConstPointer => Some(ty::adjustment::Adjust::MutToConstPointer), ty::adjustment::Adjust::Unsize => @@ -1187,7 +1187,7 @@ EnumTypeFoldableImpl! { (ty::adjustment::Adjust::NeverToAny), (ty::adjustment::Adjust::ReifyFnPointer), (ty::adjustment::Adjust::UnsafeFnPointer), - (ty::adjustment::Adjust::ClosureFnPointer), + (ty::adjustment::Adjust::ClosureFnPointer)(a), (ty::adjustment::Adjust::MutToConstPointer), (ty::adjustment::Adjust::Unsize), (ty::adjustment::Adjust::Deref)(a), diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index dfb7f37eb4cfa..53640284a2ca9 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -193,7 +193,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } } - mir::CastKind::ClosureFnPointer => { + mir::CastKind::ClosureFnPointer(_) => { match operand.layout.ty.sty { ty::Closure(def_id, substs) => { let instance = monomorphize::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 a3561515aaa31..3b559b28f1235 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1999,14 +1999,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - CastKind::ClosureFnPointer => { + CastKind::ClosureFnPointer(unsafety) => { let sig = match op.ty(mir, 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); + let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig, *unsafety); if let Err(terr) = self.eq_types( ty_fn_ptr_from, diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 9e12a7e6fa931..84f74484e606f 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -162,9 +162,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let source = unpack!(block = this.as_operand(block, scope, source)); block.and(Rvalue::Cast(CastKind::UnsafeFnPointer, source, expr.ty)) } - ExprKind::ClosureFnPointer { source } => { + ExprKind::ClosureFnPointer { source, unsafety } => { let source = unpack!(block = this.as_operand(block, scope, source)); - block.and(Rvalue::Cast(CastKind::ClosureFnPointer, source, expr.ty)) + block.and(Rvalue::Cast(CastKind::ClosureFnPointer(unsafety), source, expr.ty)) } ExprKind::MutToConstPointer { source } => { let source = unpack!(block = this.as_operand(block, scope, source)); diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index e4f92f81e9ff6..91113dc2271be 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -81,8 +81,8 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, Adjust::UnsafeFnPointer => { ExprKind::UnsafeFnPointer { source: expr.to_ref() } } - Adjust::ClosureFnPointer => { - ExprKind::ClosureFnPointer { source: expr.to_ref() } + Adjust::ClosureFnPointer(unsafety) => { + ExprKind::ClosureFnPointer { source: expr.to_ref(), unsafety } } Adjust::NeverToAny => { ExprKind::NeverToAny { source: expr.to_ref() } diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 3a38876bb6802..a661649db0fd4 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -185,6 +185,7 @@ pub enum ExprKind<'tcx> { }, ClosureFnPointer { source: ExprRef<'tcx>, + unsafety: hir::Unsafety, }, UnsafeFnPointer { source: ExprRef<'tcx>, diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index ba61b03ea673b..fe719bff250a8 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> } } - ClosureFnPointer => { + 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 0e8ab2ba2a5a2..45b346b641362 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -563,7 +563,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::ClosureFnPointer, ref operand, _) => { + mir::Rvalue::Cast(mir::CastKind::ClosureFnPointer(_), ref operand, _) => { let source_ty = operand.ty(self.mir, self.tcx); let source_ty = self.tcx.subst_and_normalize_erasing_regions( self.param_substs, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 0b9ad85e6b1c7..9bd5fce31f1a2 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1105,7 +1105,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { Rvalue::CheckedBinaryOp(..) | Rvalue::Cast(CastKind::ReifyFnPointer, ..) | Rvalue::Cast(CastKind::UnsafeFnPointer, ..) | - Rvalue::Cast(CastKind::ClosureFnPointer, ..) | + Rvalue::Cast(CastKind::ClosureFnPointer(_), ..) | Rvalue::Cast(CastKind::Unsize, ..) | Rvalue::Cast(CastKind::MutToConstPointer, ..) | Rvalue::Discriminant(..) | diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index 8742c5d759c8f..87459571b529c 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -156,7 +156,7 @@ fn check_rvalue( check_operand(tcx, mir, operand, span) } Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) | - Rvalue::Cast(CastKind::ClosureFnPointer, _, _) | + Rvalue::Cast(CastKind::ClosureFnPointer(_), _, _) | Rvalue::Cast(CastKind::ReifyFnPointer, _, _) => Err(( span, "function pointer casts are not allowed in const fn".into(), diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs index a0a0d7be1b95d..7c37c38f2d741 100644 --- a/src/librustc_passes/rvalue_promotion.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -586,7 +586,7 @@ fn check_adjustments<'a, 'tcx>( Adjust::NeverToAny | Adjust::ReifyFnPointer | Adjust::UnsafeFnPointer | - Adjust::ClosureFnPointer | + Adjust::ClosureFnPointer(_) | Adjust::MutToConstPointer | Adjust::Borrow(_) | Adjust::Unsize => {} diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index ac8b639edbfa3..c470bc09e8cd0 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -225,7 +225,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { } ty::Closure(def_id_a, substs_a) => { // Non-capturing closures are coercible to - // function pointers + // function pointers or unsafe function pointers. + // It cannot convert closures that require unsafe. self.coerce_closure_to_fn(a, def_id_a, substs_a, b) } _ => { @@ -714,16 +715,19 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { let hir_id_a = self.tcx.hir().as_local_hir_id(def_id_a).unwrap(); match b.sty { - ty::FnPtr(_) if self.tcx.with_freevars(hir_id_a, |v| v.is_empty()) => { + ty::FnPtr(fn_ty) if self.tcx.with_freevars(hir_id_a, |v| v.is_empty()) => { // We coerce the closure, which has fn type // `extern "rust-call" fn((arg0,arg1,...)) -> _` // to // `fn(arg0,arg1,...) -> _` + // or + // `unsafe fn(arg0,arg1,...) -> _` let sig = self.closure_sig(def_id_a, substs_a); - let pointer_ty = self.tcx.coerce_closure_fn_ty(sig); + let unsafety = fn_ty.unsafety(); + let pointer_ty = self.tcx.coerce_closure_fn_ty(sig, unsafety); debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); - self.unify_and(pointer_ty, b, simple(Adjust::ClosureFnPointer)) + self.unify_and(pointer_ty, b, simple(Adjust::ClosureFnPointer(unsafety))) } _ => 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 new file mode 100644 index 0000000000000..36777693faba0 --- /dev/null +++ b/src/test/compile-fail/coerce-unsafe-closure-to-unsafe-fn-ptr.rs @@ -0,0 +1,5 @@ +fn main() { + let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; + //~^ ERROR E0133 + let _: unsafe fn() = || unsafe { ::std::pin::Pin::new_unchecked(&0_u8); }; // OK +} diff --git a/src/test/run-pass/typeck-closure-to-unsafe-fn-ptr.rs b/src/test/run-pass/typeck-closure-to-unsafe-fn-ptr.rs new file mode 100644 index 0000000000000..fe15b912d6029 --- /dev/null +++ b/src/test/run-pass/typeck-closure-to-unsafe-fn-ptr.rs @@ -0,0 +1,7 @@ +unsafe fn call_unsafe(func: unsafe fn() -> ()) -> () { + func() +} + +pub fn main() { + unsafe { call_unsafe(|| {}); } +}