Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow closure to extern fn pointer coercion #61528

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/librustc/ty/adjustment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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,
Expand Down
15 changes: 7 additions & 8 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2420,21 +2420,20 @@ 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) => {
params.into_iter().map(|k| k.expect_ty())
}
_ => 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)
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_codegen_ssa/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(_, _)) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong, it needs a shim to work correctly. I think we should consider having a coercion from a Rust function item (not a fn pointer) to an extern "..." fn pointer.

Then the closure coercion would still be "like coercing <typeof(|| ...) as FnOnce<_>>::call_once, modulo first argument", and they would both have the extra capability of switching ABI.

match operand.layout.ty.sty {
ty::Closure(def_id, substs) => {
let instance = Instance::resolve_closure(
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/monomorphize/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/qualify_min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
5 changes: 3 additions & 2 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// -------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax_pos/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ symbols! {
Clone,
clone_closures,
clone_from,
closure_to_extern_fn_coercion,
closure_to_fn_coercion,
cmp,
cmpxchg16b_target_feature,
Expand Down
Original file line number Diff line number Diff line change
@@ -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(|| {});
}
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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
}
}