]> git.lizzy.rs Git - rust.git/commitdiff
Support coercion between (FnDef | Closure) and (FnDef | Closure) when Closure is...
authorDonough Liu <ldm2993593805@163.com>
Thu, 7 May 2020 05:11:24 +0000 (13:11 +0800)
committerDonough Liu <ldm2993593805@163.com>
Sat, 9 May 2020 01:33:15 +0000 (09:33 +0800)
src/librustc_middle/ty/context.rs
src/librustc_mir/borrow_check/type_check/mod.rs
src/librustc_typeck/check/coercion.rs

index e43eb01ad96f7edae3fe096a87831fb56985830b..5f01c88d413f8cd03982ce4d7a32448c5d682554 100644 (file)
@@ -2056,24 +2056,25 @@ pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> {
         self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig }))
     }
 
-    /// Given a closure signature `sig`, returns an equivalent `fn`
-    /// 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)`.
-    /// `unsafety` determines the unsafety of the `fn` type. If you pass
+    /// Given a closure signature, returns an equivalent fn 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)`.
+    /// `unsafety` determines the unsafety of the fn signature. 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| {
+    pub fn signature_unclosure(
+        self,
+        sig: PolyFnSig<'tcx>,
+        unsafety: hir::Unsafety,
+    ) -> PolyFnSig<'tcx> {
+        sig.map_bound(|s| {
             let params_iter = match s.inputs()[0].kind {
                 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_ptr(converted_sig)
+        })
     }
 
     #[allow(rustc::usage_of_ty_tykind)]
index 7533bdfbd8d6067917d4c49581307ee699d0c4e5..edab96badc8587db0e247fda24a8bf9031f316a7 100644 (file)
@@ -2087,7 +2087,7 @@ fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: L
                             ty::Closure(_, substs) => substs.as_closure().sig(),
                             _ => bug!(),
                         };
-                        let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig, *unsafety);
+                        let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety));
 
                         if let Err(terr) = self.eq_types(
                             ty_fn_ptr_from,
index 86cafa0b8ca2500ef84bedce90abd1339a43660f..a8093e7638d02fc97252a4f8c5d9a593e9c62529 100644 (file)
@@ -759,7 +759,8 @@ fn coerce_closure_to_fn(
                 //     `unsafe fn(arg0,arg1,...) -> _`
                 let closure_sig = substs_a.as_closure().sig();
                 let unsafety = fn_ty.unsafety();
-                let pointer_ty = self.tcx.coerce_closure_fn_ty(closure_sig, unsafety);
+                let pointer_ty =
+                    self.tcx.mk_fn_ptr(self.tcx.signature_unclosure(closure_sig, unsafety));
                 debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
                 self.unify_and(
                     pointer_ty,
@@ -875,23 +876,63 @@ fn try_find_coercion_lub<E>(
         debug!("coercion::try_find_coercion_lub({:?}, {:?})", prev_ty, new_ty);
 
         // Special-case that coercion alone cannot handle:
-        // Two function item types of differing IDs or InternalSubsts.
-        if let (&ty::FnDef(..), &ty::FnDef(..)) = (&prev_ty.kind, &new_ty.kind) {
-            // Don't reify if the function types have a LUB, i.e., they
-            // are the same function and their parameters have a LUB.
-            let lub_ty = self
-                .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
-                .map(|ok| self.register_infer_ok_obligations(ok));
-
-            if lub_ty.is_ok() {
-                // We have a LUB of prev_ty and new_ty, just return it.
-                return lub_ty;
+        // Function items or non-capturing closures of differing IDs or InternalSubsts.
+        let (a_sig, b_sig) = {
+            let is_capturing_closure = |ty| {
+                if let &ty::Closure(_, substs) = ty {
+                    substs.as_closure().upvar_tys().next().is_some()
+                } else {
+                    false
+                }
+            };
+            if is_capturing_closure(&prev_ty.kind) || is_capturing_closure(&new_ty.kind) {
+                (None, None)
+            } else {
+                match (&prev_ty.kind, &new_ty.kind) {
+                    (&ty::FnDef(..), &ty::FnDef(..)) => {
+                        // Don't reify if the function types have a LUB, i.e., they
+                        // are the same function and their parameters have a LUB.
+                        match self
+                            .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
+                        {
+                            // We have a LUB of prev_ty and new_ty, just return it.
+                            Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)),
+                            Err(_) => {
+                                (Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx)))
+                            }
+                        }
+                    }
+                    (&ty::Closure(_, substs), &ty::FnDef(..)) => {
+                        let b_sig = new_ty.fn_sig(self.tcx);
+                        let a_sig = self
+                            .tcx
+                            .signature_unclosure(substs.as_closure().sig(), b_sig.unsafety());
+                        (Some(a_sig), Some(b_sig))
+                    }
+                    (&ty::FnDef(..), &ty::Closure(_, substs)) => {
+                        let a_sig = prev_ty.fn_sig(self.tcx);
+                        let b_sig = self
+                            .tcx
+                            .signature_unclosure(substs.as_closure().sig(), a_sig.unsafety());
+                        (Some(a_sig), Some(b_sig))
+                    }
+                    (&ty::Closure(_, substs_a), &ty::Closure(_, substs_b)) => (
+                        Some(self.tcx.signature_unclosure(
+                            substs_a.as_closure().sig(),
+                            hir::Unsafety::Normal,
+                        )),
+                        Some(self.tcx.signature_unclosure(
+                            substs_b.as_closure().sig(),
+                            hir::Unsafety::Normal,
+                        )),
+                    ),
+                    _ => (None, None),
+                }
             }
-
+        };
+        if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
             // The signature must match.
-            let a_sig = prev_ty.fn_sig(self.tcx);
             let a_sig = self.normalize_associated_types_in(new.span, &a_sig);
-            let b_sig = new_ty.fn_sig(self.tcx);
             let b_sig = self.normalize_associated_types_in(new.span, &b_sig);
             let sig = self
                 .at(cause, self.param_env)
@@ -901,17 +942,23 @@ fn try_find_coercion_lub<E>(
 
             // Reify both sides and return the reified fn pointer type.
             let fn_ptr = self.tcx.mk_fn_ptr(sig);
-            for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) {
-                // The only adjustment that can produce an fn item is
-                // `NeverToAny`, so this should always be valid.
+            let prev_adjustment = match prev_ty.kind {
+                ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())),
+                ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
+                _ => unreachable!(),
+            };
+            let next_adjustment = match new_ty.kind {
+                ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())),
+                ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
+                _ => unreachable!(),
+            };
+            for expr in exprs.iter().map(|e| e.as_coercion_site()) {
                 self.apply_adjustments(
                     expr,
-                    vec![Adjustment {
-                        kind: Adjust::Pointer(PointerCast::ReifyFnPointer),
-                        target: fn_ptr,
-                    }],
+                    vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }],
                 );
             }
+            self.apply_adjustments(new, vec![Adjustment { kind: next_adjustment, target: fn_ptr }]);
             return Ok(fn_ptr);
         }