Skip to content

Commit ad5b9a7

Browse files
committed
Support coercion between (FnDef | Closure) and (FnDef | Closure)
1 parent ea733c3 commit ad5b9a7

File tree

3 files changed

+62
-33
lines changed

3 files changed

+62
-33
lines changed

src/librustc_middle/ty/context.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2056,24 +2056,25 @@ impl<'tcx> TyCtxt<'tcx> {
20562056
self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig }))
20572057
}
20582058

2059-
/// Given a closure signature `sig`, returns an equivalent `fn`
2060-
/// type with the same signature. Detuples and so forth -- so
2061-
/// e.g., if we have a sig with `Fn<(u32, i32)>` then you would get
2062-
/// a `fn(u32, i32)`.
2063-
/// `unsafety` determines the unsafety of the `fn` type. If you pass
2059+
/// Given a closure signature, returns an equivalent fn signature. Detuples
2060+
/// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then
2061+
/// you would get a `fn(u32, i32)`.
2062+
/// `unsafety` determines the unsafety of the fn signature. If you pass
20642063
/// `hir::Unsafety::Unsafe` in the previous example, then you would get
20652064
/// an `unsafe fn (u32, i32)`.
20662065
/// It cannot convert a closure that requires unsafe.
2067-
pub fn coerce_closure_fn_ty(self, sig: PolyFnSig<'tcx>, unsafety: hir::Unsafety) -> Ty<'tcx> {
2068-
let converted_sig = sig.map_bound(|s| {
2066+
pub fn signature_unclosure(
2067+
self,
2068+
sig: PolyFnSig<'tcx>,
2069+
unsafety: hir::Unsafety,
2070+
) -> PolyFnSig<'tcx> {
2071+
sig.map_bound(|s| {
20692072
let params_iter = match s.inputs()[0].kind {
20702073
ty::Tuple(params) => params.into_iter().map(|k| k.expect_ty()),
20712074
_ => bug!(),
20722075
};
20732076
self.mk_fn_sig(params_iter, s.output(), s.c_variadic, unsafety, abi::Abi::Rust)
2074-
});
2075-
2076-
self.mk_fn_ptr(converted_sig)
2077+
})
20772078
}
20782079

20792080
#[allow(rustc::usage_of_ty_tykind)]

src/librustc_mir/borrow_check/type_check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2087,7 +2087,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
20872087
ty::Closure(_, substs) => substs.as_closure().sig(),
20882088
_ => bug!(),
20892089
};
2090-
let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig, *unsafety);
2090+
let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety));
20912091

20922092
if let Err(terr) = self.eq_types(
20932093
ty_fn_ptr_from,

src/librustc_typeck/check/coercion.rs

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
759759
// `unsafe fn(arg0,arg1,...) -> _`
760760
let closure_sig = substs_a.as_closure().sig();
761761
let unsafety = fn_ty.unsafety();
762-
let pointer_ty = self.tcx.coerce_closure_fn_ty(closure_sig, unsafety);
762+
let pointer_ty =
763+
self.tcx.mk_fn_ptr(self.tcx.signature_unclosure(closure_sig, unsafety));
763764
debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
764765
self.unify_and(
765766
pointer_ty,
@@ -875,23 +876,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
875876
debug!("coercion::try_find_coercion_lub({:?}, {:?})", prev_ty, new_ty);
876877

877878
// Special-case that coercion alone cannot handle:
878-
// Two function item types of differing IDs or InternalSubsts.
879-
if let (&ty::FnDef(..), &ty::FnDef(..)) = (&prev_ty.kind, &new_ty.kind) {
880-
// Don't reify if the function types have a LUB, i.e., they
881-
// are the same function and their parameters have a LUB.
882-
let lub_ty = self
883-
.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
884-
.map(|ok| self.register_infer_ok_obligations(ok));
885-
886-
if lub_ty.is_ok() {
887-
// We have a LUB of prev_ty and new_ty, just return it.
888-
return lub_ty;
879+
// Function items or Closures of differing IDs or InternalSubsts.
880+
let (a_sig, b_sig) = match (&prev_ty.kind, &new_ty.kind) {
881+
(&ty::FnDef(..), &ty::FnDef(..)) => {
882+
// Don't reify if the function types have a LUB, i.e., they
883+
// are the same function and their parameters have a LUB.
884+
match self.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) {
885+
// We have a LUB of prev_ty and new_ty, just return it.
886+
Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)),
887+
Err(_) => (Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx))),
888+
}
889889
}
890-
890+
(&ty::Closure(_, substs), &ty::FnDef(..)) => {
891+
let b_sig = new_ty.fn_sig(self.tcx);
892+
let a_sig =
893+
self.tcx.signature_unclosure(substs.as_closure().sig(), b_sig.unsafety());
894+
(Some(a_sig), Some(b_sig))
895+
}
896+
(&ty::FnDef(..), &ty::Closure(_, substs)) => {
897+
let a_sig = prev_ty.fn_sig(self.tcx);
898+
let b_sig =
899+
self.tcx.signature_unclosure(substs.as_closure().sig(), a_sig.unsafety());
900+
(Some(a_sig), Some(b_sig))
901+
}
902+
(&ty::Closure(_, substs_a), &ty::Closure(_, substs_b)) => (
903+
Some(
904+
self.tcx
905+
.signature_unclosure(substs_a.as_closure().sig(), hir::Unsafety::Normal),
906+
),
907+
Some(
908+
self.tcx
909+
.signature_unclosure(substs_b.as_closure().sig(), hir::Unsafety::Normal),
910+
),
911+
),
912+
_ => (None, None),
913+
};
914+
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
891915
// The signature must match.
892-
let a_sig = prev_ty.fn_sig(self.tcx);
893916
let a_sig = self.normalize_associated_types_in(new.span, &a_sig);
894-
let b_sig = new_ty.fn_sig(self.tcx);
895917
let b_sig = self.normalize_associated_types_in(new.span, &b_sig);
896918
let sig = self
897919
.at(cause, self.param_env)
@@ -901,17 +923,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
901923

902924
// Reify both sides and return the reified fn pointer type.
903925
let fn_ptr = self.tcx.mk_fn_ptr(sig);
904-
for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) {
905-
// The only adjustment that can produce an fn item is
906-
// `NeverToAny`, so this should always be valid.
926+
let prev_adjustment = match prev_ty.kind {
927+
ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())),
928+
ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
929+
_ => unreachable!(),
930+
};
931+
let next_adjustment = match new_ty.kind {
932+
ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())),
933+
ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
934+
_ => unreachable!(),
935+
};
936+
for expr in exprs.iter().map(|e| e.as_coercion_site()) {
907937
self.apply_adjustments(
908938
expr,
909-
vec![Adjustment {
910-
kind: Adjust::Pointer(PointerCast::ReifyFnPointer),
911-
target: fn_ptr,
912-
}],
939+
vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }],
913940
);
914941
}
942+
self.apply_adjustments(new, vec![Adjustment { kind: next_adjustment, target: fn_ptr }]);
915943
return Ok(fn_ptr);
916944
}
917945

0 commit comments

Comments
 (0)