about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-11-05 16:49:08 +0000
committerbors <bors@rust-lang.org>2017-11-05 16:49:08 +0000
commit666687a68cb9c42bf5eadcb2e5e447d7de5190d5 (patch)
tree47afa62a461e46d465dca7fb8b1a0c1cf6023f3c
parentb55d29095646f24461de8adebe867986863f8c46 (diff)
parente8a96c97f4972c3d4823105f53e28b88cd9b533e (diff)
downloadrust-666687a68cb9c42bf5eadcb2e5e447d7de5190d5.tar.gz
rust-666687a68cb9c42bf5eadcb2e5e447d7de5190d5.zip
Auto merge of #45072 - nikomatsakis:issue-38714, r=arielb1
new rules for merging expected and supplied types in closure signatures

As uncovered in #38714, we currently have some pretty bogus code for combining the "expected signature" of a closure with the "supplied signature". To set the scene, consider a case like this:

```rust
fn foo<F>(f: F)
where
  F: for<'a> FnOnce(&'a u32) -> &'a u32
  // ^ *expected* signature comes from this where-clause
{
    ...
}

fn main() {
    foo(|x: &u32| -> &u32 { .. }
     // ^^^^^^^^^^^^^^^^^ supplied signature
     // comes from here
}
```

In this case, the supplied signature (a) includes all the parts and (b) is the same as the expected signature, modulo the names used for the regions. But often people supply only *some* parts of the signature. For example, one might write `foo(|x| ..)`, leaving *everything* to be inferred, or perhaps `foo(|x: &u32| ...)`, which leaves the return type to be inferred.

In the current code, we use the expected type to supply the types that are not given, but otherwise use the type the user gave, except for one case: if the user writes `fn foo(|x: _| ..)` (i.e., an underscore at the outermost level), then we will take the expected type (rather than instantiating a fresh type variable). This can result in nonsensical situations, particularly with bound regions that link the types of parameters to one another or to the return type. Consider `foo(|x: &u32| ...)` -- if we *literally* splice the expected return type of `&'a u32` together with what the user gave, we wind up with a signature like `for<'a> fn(&u32) -> &'a u32`. This is not even permitted as a type, because bound regions like `'a` must appear also in the arguments somewhere, which is why #38714 leads to an ICE.

This PR institutes some new rules. These are not meant to be the *final* set of rules, but they are a kind of "lower bar" for what kind of code we accept (i.e., we can extend these rules in the future to be smarter in some cases, but -- as we will see -- these rules do accept some things that we then would not be able to back off from).

These rules are derived from a few premises:

- First and foremost, anonymous regions in closure annotation are mostly requests for the code to "figure out the right lifetime" and shouldn't be read too closely. So for example when people write a closure signature like `|x: &u32|`, they are really intended for us to "figure out" the right region for `x`.
    - In contrast, the current code treats this supplied type as being more definitive. In particular, writing `|x: &u32|` would always result in the region of `x` being bound in the closure type. In other words, the signature would be something like `for<'a> fn(&'a u32)` -- this is derived from the fact that `fn(&u32)` expands to a type where the region is bound in the fn type.
    - This PR takes a different approach. The "binding level" for reference types appearing in closure signatures can be informed in some cases by the expected signature. So, for example, if the expected signature is something like `(&'f u32)`, where the region of the first argument appears free, then for `|x: &u32|`, the new code would infer `x` to also have the free region `'f`.
        - This inference has some limits. We don't do this for bindings that appear within the selected types themselves. So e.g. `|x: fn(&u32)|`, when combined with an expected type of `fn(fn(&'f u32))`, would still result in a closure that expects `for<'a> fn(&'a u32)`. Such an annotation will ultimately result in an error, as it happens, since `foo` is supplying a `fn(&'f u32)` to the closure, but the closure signature demands a `for<'a> fn(&'a u32)`. But still we choose to trust it and have the user change it.
        - I wanted to preserve the rough intuition that one can copy-and-paste a type out of the fn signature and into the fn body without dramatically changing its meaning. Interestingly, if one has `|x: &u32|`, then regardless of whether the region of `x` is bound or free in the closure signature, it is also free in the region body, and that is also true when one writes `let x: &u32`, so that intuition holds here. But the same would not be true for `fn(&u32)`, hence the different behavior.
- Second, we must take either **all** the references to bound regions from the expected type or **none**. The current code, as we saw, will happily take a bound region in the return type but drop the other place where it is used, in the parameters. Since bound regions are all about linking multiple things together, I think it's important not to do that. (That said, we could conceivably be a bit less strict here, since the subtyping rules will get our back, but we definitely don't want any bound regions that appear only in the return type.)
- Finally, we cannot take the bound region names from the supplied types and "intermix" them with the names from the expected types.
    - We *could* potentially do some alpha renaming, but I didn't do that.
- Ultimately, if the types the user supplied do not match expectations in some way that we cannot recover from, we fallback to deriving the closure signature solely from those expected types.
    - For example, if the expected type is `u32` but the user wrote `i32`.
    - Or, more subtle, if the user wrote e.g. `&'x u32` for some named lifetime `'x`, but the expected type includes a bound lifetime (`for<'a> (&'a u32)`). In that case, preferring the type that the user explicitly wrote would hide an appearance of a bound name from the expected type, and we try to never do that.

The detailed rules that I came up with are found in the code, but for ease of reading I've also [excerpted them into a gist](https://gist.github.com/nikomatsakis/e69252a2b57e6d97d044c2f254c177f1). I am not convinced they are correct and would welcome feedback for alternative approaches.

(As an aside, the way I think I would ultimately *prefer* to think about this is that the conversion from HIR types to internal types could be parameterized by an "expected type" that it uses to guide itself. However, since that would be a pain, I opted *in the code* to first instantiate the supplied types as `Ty<'tcx>` and then "merge" those types with the `Ty<'tcx>` from the expected signature.)

I think we should probably FCP this before landing.

cc @rust-lang/lang
r? @arielb1
-rw-r--r--src/librustc/infer/higher_ranked/mod.rs5
-rw-r--r--src/librustc_typeck/astconv.rs54
-rw-r--r--src/librustc_typeck/check/closure.rs483
-rw-r--r--src/librustc_typeck/lib.rs1
-rw-r--r--src/test/compile-fail/closure-expected-type/README.md1
-rw-r--r--src/test/compile-fail/closure-expected-type/expect-fn-supply-fn-multiple.rs49
-rw-r--r--src/test/compile-fail/closure-expected-type/expect-fn-supply-fn.rs70
-rw-r--r--src/test/compile-fail/closure-expected-type/expect-infer-var-appearing-twice.rs35
-rw-r--r--src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-bound-region.rs29
-rw-r--r--src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-free-region.rs29
-rw-r--r--src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs80
-rw-r--r--src/test/compile-fail/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.rs29
-rw-r--r--src/test/mir-opt/validate_1.rs2
-rw-r--r--src/test/mir-opt/validate_4.rs4
-rw-r--r--src/test/mir-opt/validate_5.rs2
-rw-r--r--src/test/run-pass/closure-expected-type/README.md8
-rw-r--r--src/test/run-pass/closure-expected-type/expect-infer-supply-two-infers.rs26
-rw-r--r--src/test/run-pass/closure-expected-type/issue-38714.rs26
-rw-r--r--src/test/run-pass/closure-expected-type/supply-just-return-type.rs35
-rw-r--r--src/test/run-pass/closure-expected-type/supply-nothing.rs20
-rw-r--r--src/test/ui/mismatched_types/closure-arg-count.stderr19
21 files changed, 838 insertions, 169 deletions
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index 0d02420457e..6736751a5a2 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -155,7 +155,10 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
                                .filter(|&&r| !skol_resolution_map.contains_key(r))
                                .cloned()
                                .next()
-                               .expect("no representative region");
+                               .unwrap_or_else(|| {
+                                   bug!("no representative region for `{:?}` in `{:?}`",
+                                        skol, regions)
+                               });
 
                     (skol, representative)
                 })
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index c7f7e62fd61..1471e235156 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -1206,60 +1206,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
         bare_fn_ty
     }
 
-    pub fn ty_of_closure(&self,
-        unsafety: hir::Unsafety,
-        decl: &hir::FnDecl,
-        abi: abi::Abi,
-        expected_sig: Option<ty::FnSig<'tcx>>)
-        -> ty::PolyFnSig<'tcx>
-    {
-        debug!("ty_of_closure(expected_sig={:?})",
-               expected_sig);
-
-        let input_tys = decl.inputs.iter().enumerate().map(|(i, a)| {
-            let expected_arg_ty = expected_sig.as_ref().and_then(|e| {
-                // no guarantee that the correct number of expected args
-                // were supplied
-                if i < e.inputs().len() {
-                    Some(e.inputs()[i])
-                } else {
-                    None
-                }
-            });
-            self.ty_of_arg(a, expected_arg_ty)
-        });
-
-        let expected_ret_ty = expected_sig.as_ref().map(|e| e.output());
-
-        let output_ty = match decl.output {
-            hir::Return(ref output) => {
-                if let (&hir::TyInfer, Some(expected_ret_ty)) = (&output.node, expected_ret_ty) {
-                    self.record_ty(output.hir_id, expected_ret_ty, output.span);
-                    expected_ret_ty
-                } else {
-                    self.ast_ty_to_ty(&output)
-                }
-            }
-            hir::DefaultReturn(span) => {
-                if let Some(expected_ret_ty) = expected_ret_ty {
-                    expected_ret_ty
-                } else {
-                    self.ty_infer(span)
-                }
-            }
-        };
-
-        debug!("ty_of_closure: output_ty={:?}", output_ty);
-
-        ty::Binder(self.tcx().mk_fn_sig(
-            input_tys,
-            output_ty,
-            decl.variadic,
-            unsafety,
-            abi
-        ))
-    }
-
     /// Given the bounds on an object, determines what single region bound (if any) we can
     /// use to summarize this type. The basic idea is that we will use the bound the user
     /// provided, if they provided one, and otherwise search the supertypes of trait bounds
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index 07159770d5b..d475fb0cf1a 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -13,25 +13,37 @@
 use super::{check_fn, Expectation, FnCtxt};
 
 use astconv::AstConv;
+use rustc::hir::def_id::DefId;
+use rustc::infer::{InferOk, InferResult};
+use rustc::infer::LateBoundRegionConversionTime;
 use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc::ty::{self, ToPolyTraitRef, Ty};
 use rustc::ty::subst::Substs;
+use rustc::ty::TypeFoldable;
 use std::cmp;
 use std::iter;
 use syntax::abi::Abi;
 use rustc::hir;
 
+struct ClosureSignatures<'tcx> {
+    bound_sig: ty::PolyFnSig<'tcx>,
+    liberated_sig: ty::FnSig<'tcx>,
+}
+
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
-    pub fn check_expr_closure(&self,
-                              expr: &hir::Expr,
-                              _capture: hir::CaptureClause,
-                              decl: &'gcx hir::FnDecl,
-                              body_id: hir::BodyId,
-                              expected: Expectation<'tcx>)
-                              -> Ty<'tcx> {
-        debug!("check_expr_closure(expr={:?},expected={:?})",
-               expr,
-               expected);
+    pub fn check_expr_closure(
+        &self,
+        expr: &hir::Expr,
+        _capture: hir::CaptureClause,
+        decl: &'gcx hir::FnDecl,
+        body_id: hir::BodyId,
+        expected: Expectation<'tcx>,
+    ) -> Ty<'tcx> {
+        debug!(
+            "check_expr_closure(expr={:?},expected={:?})",
+            expr,
+            expected
+        );
 
         // It's always helpful for inference if we know the kind of
         // closure sooner rather than later, so first examine the expected
@@ -44,70 +56,84 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         self.check_closure(expr, expected_kind, decl, body, expected_sig)
     }
 
-    fn check_closure(&self,
-                     expr: &hir::Expr,
-                     opt_kind: Option<ty::ClosureKind>,
-                     decl: &'gcx hir::FnDecl,
-                     body: &'gcx hir::Body,
-                     expected_sig: Option<ty::FnSig<'tcx>>)
-                     -> Ty<'tcx> {
-        debug!("check_closure opt_kind={:?} expected_sig={:?}",
-               opt_kind,
-               expected_sig);
+    fn check_closure(
+        &self,
+        expr: &hir::Expr,
+        opt_kind: Option<ty::ClosureKind>,
+        decl: &'gcx hir::FnDecl,
+        body: &'gcx hir::Body,
+        expected_sig: Option<ty::FnSig<'tcx>>,
+    ) -> Ty<'tcx> {
+        debug!(
+            "check_closure(opt_kind={:?}, expected_sig={:?})",
+            opt_kind,
+            expected_sig
+        );
 
         let expr_def_id = self.tcx.hir.local_def_id(expr.id);
-        let sig = AstConv::ty_of_closure(self,
-                                         hir::Unsafety::Normal,
-                                         decl,
-                                         Abi::RustCall,
-                                         expected_sig);
-        // `deduce_expectations_from_expected_type` introduces late-bound
-        // lifetimes defined elsewhere, which we need to anonymize away.
-        let sig = self.tcx.anonymize_late_bound_regions(&sig);
+
+        let ClosureSignatures {
+            bound_sig,
+            liberated_sig,
+        } = self.sig_of_closure(expr_def_id, decl, body, expected_sig);
+
+        debug!("check_closure: ty_of_closure returns {:?}", liberated_sig);
+
+        let interior = check_fn(
+            self,
+            self.param_env,
+            liberated_sig,
+            decl,
+            expr.id,
+            body,
+            true,
+        ).1;
 
         // Create type variables (for now) to represent the transformed
         // types of upvars. These will be unified during the upvar
         // inference phase (`upvar.rs`).
-        let base_substs = Substs::identity_for_item(self.tcx,
-            self.tcx.closure_base_def_id(expr_def_id));
-        let substs = base_substs.extend_to(self.tcx, expr_def_id,
-                |_, _| span_bug!(expr.span, "closure has region param"),
-                |_, _| self.infcx.next_ty_var(TypeVariableOrigin::TransformedUpvar(expr.span))
+        let base_substs =
+            Substs::identity_for_item(self.tcx, self.tcx.closure_base_def_id(expr_def_id));
+        let substs = base_substs.extend_to(
+            self.tcx,
+            expr_def_id,
+            |_, _| span_bug!(expr.span, "closure has region param"),
+            |_, _| {
+                self.infcx
+                    .next_ty_var(TypeVariableOrigin::TransformedUpvar(expr.span))
+            },
         );
-
-        let fn_sig = self.liberate_late_bound_regions(expr_def_id, &sig);
-        let fn_sig = self.inh.normalize_associated_types_in(body.value.span,
-                                                            body.value.id,
-                                                            self.param_env,
-                                                            &fn_sig);
-
-        let interior = check_fn(self, self.param_env, fn_sig, decl, expr.id, body, true).1;
+        let closure_type = self.tcx.mk_closure(expr_def_id, substs);
 
         if let Some(interior) = interior {
-            let closure_substs = ty::ClosureSubsts {
-                substs: substs,
-            };
+            let closure_substs = ty::ClosureSubsts { substs: substs };
             return self.tcx.mk_generator(expr_def_id, closure_substs, interior);
         }
 
-        let closure_type = self.tcx.mk_closure(expr_def_id, substs);
-
-        debug!("check_closure: expr.id={:?} closure_type={:?}", expr.id, closure_type);
+        debug!(
+            "check_closure: expr.id={:?} closure_type={:?}",
+            expr.id,
+            closure_type
+        );
 
         // Tuple up the arguments and insert the resulting function type into
         // the `closures` table.
-        let sig = sig.map_bound(|sig| self.tcx.mk_fn_sig(
-            iter::once(self.tcx.intern_tup(sig.inputs(), false)),
-            sig.output(),
-            sig.variadic,
-            sig.unsafety,
-            sig.abi
-        ));
-
-        debug!("closure for {:?} --> sig={:?} opt_kind={:?}",
-               expr_def_id,
-               sig,
-               opt_kind);
+        let sig = bound_sig.map_bound(|sig| {
+            self.tcx.mk_fn_sig(
+                iter::once(self.tcx.intern_tup(sig.inputs(), false)),
+                sig.output(),
+                sig.variadic,
+                sig.unsafety,
+                sig.abi,
+            )
+        });
+
+        debug!(
+            "check_closure: expr_def_id={:?}, sig={:?}, opt_kind={:?}",
+            expr_def_id,
+            sig,
+            opt_kind
+        );
 
         {
             let mut tables = self.tables.borrow_mut();
@@ -123,22 +149,26 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         closure_type
     }
 
-    fn deduce_expectations_from_expected_type
-        (&self,
-         expected_ty: Ty<'tcx>)
-         -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
-        debug!("deduce_expectations_from_expected_type(expected_ty={:?})",
-               expected_ty);
+    fn deduce_expectations_from_expected_type(
+        &self,
+        expected_ty: Ty<'tcx>,
+    ) -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
+        debug!(
+            "deduce_expectations_from_expected_type(expected_ty={:?})",
+            expected_ty
+        );
 
         match expected_ty.sty {
             ty::TyDynamic(ref object_type, ..) => {
-                let sig = object_type.projection_bounds()
+                let sig = object_type
+                    .projection_bounds()
                     .filter_map(|pb| {
                         let pb = pb.with_self_ty(self.tcx, self.tcx.types.err);
                         self.deduce_sig_from_projection(&pb)
                     })
                     .next();
-                let kind = object_type.principal()
+                let kind = object_type
+                    .principal()
                     .and_then(|p| self.tcx.lang_items().fn_trait_kind(p.def_id()));
                 (sig, kind)
             }
@@ -148,19 +178,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn deduce_expectations_from_obligations
-        (&self,
-         expected_vid: ty::TyVid)
-         -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
+    fn deduce_expectations_from_obligations(
+        &self,
+        expected_vid: ty::TyVid,
+    ) -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
         let fulfillment_cx = self.fulfillment_cx.borrow();
         // Here `expected_ty` is known to be a type inference variable.
 
-        let expected_sig = fulfillment_cx.pending_obligations()
+        let expected_sig = fulfillment_cx
+            .pending_obligations()
             .iter()
             .map(|obligation| &obligation.obligation)
             .filter_map(|obligation| {
-                debug!("deduce_expectations_from_obligations: obligation.predicate={:?}",
-                       obligation.predicate);
+                debug!(
+                    "deduce_expectations_from_obligations: obligation.predicate={:?}",
+                    obligation.predicate
+                );
 
                 match obligation.predicate {
                     // Given a Projection predicate, we can potentially infer
@@ -179,7 +212,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // infer the kind. This can occur if there is a trait-reference
         // like `F : Fn<A>`. Note that due to subtyping we could encounter
         // many viable options, so pick the most restrictive.
-        let expected_kind = fulfillment_cx.pending_obligations()
+        let expected_kind = fulfillment_cx
+            .pending_obligations()
             .iter()
             .map(|obligation| &obligation.obligation)
             .filter_map(|obligation| {
@@ -204,20 +238,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     // inference variable.
                     ty::Predicate::ClosureKind(..) => None,
                 };
-                opt_trait_ref.and_then(|tr| self.self_type_matches_expected_vid(tr, expected_vid))
+                opt_trait_ref
+                    .and_then(|tr| self.self_type_matches_expected_vid(tr, expected_vid))
                     .and_then(|tr| self.tcx.lang_items().fn_trait_kind(tr.def_id()))
             })
-            .fold(None,
-                  |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur))));
+            .fold(None, |best, cur| {
+                Some(best.map_or(cur, |best| cmp::min(best, cur)))
+            });
 
         (expected_sig, expected_kind)
     }
 
     /// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
     /// everything we need to know about a closure.
-    fn deduce_sig_from_projection(&self,
-                                  projection: &ty::PolyProjectionPredicate<'tcx>)
-                                  -> Option<ty::FnSig<'tcx>> {
+    fn deduce_sig_from_projection(
+        &self,
+        projection: &ty::PolyProjectionPredicate<'tcx>,
+    ) -> Option<ty::FnSig<'tcx>> {
         let tcx = self.tcx;
 
         debug!("deduce_sig_from_projection({:?})", projection);
@@ -230,8 +267,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         let arg_param_ty = trait_ref.substs().type_at(1);
         let arg_param_ty = self.resolve_type_vars_if_possible(&arg_param_ty);
-        debug!("deduce_sig_from_projection: arg_param_ty {:?}",
-               arg_param_ty);
+        debug!(
+            "deduce_sig_from_projection: arg_param_ty {:?}",
+            arg_param_ty
+        );
 
         let input_tys = match arg_param_ty.sty {
             ty::TyTuple(tys, _) => tys.into_iter(),
@@ -242,31 +281,291 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         let ret_param_ty = projection.0.ty;
         let ret_param_ty = self.resolve_type_vars_if_possible(&ret_param_ty);
-        debug!("deduce_sig_from_projection: ret_param_ty {:?}", ret_param_ty);
+        debug!(
+            "deduce_sig_from_projection: ret_param_ty {:?}",
+            ret_param_ty
+        );
 
         let fn_sig = self.tcx.mk_fn_sig(
             input_tys.cloned(),
             ret_param_ty,
             false,
             hir::Unsafety::Normal,
-            Abi::Rust
+            Abi::Rust,
         );
         debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig);
 
         Some(fn_sig)
     }
 
-    fn self_type_matches_expected_vid(&self,
-                                      trait_ref: ty::PolyTraitRef<'tcx>,
-                                      expected_vid: ty::TyVid)
-                                      -> Option<ty::PolyTraitRef<'tcx>> {
+    fn self_type_matches_expected_vid(
+        &self,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        expected_vid: ty::TyVid,
+    ) -> Option<ty::PolyTraitRef<'tcx>> {
         let self_ty = self.shallow_resolve(trait_ref.self_ty());
-        debug!("self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
-               trait_ref,
-               self_ty);
+        debug!(
+            "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
+            trait_ref,
+            self_ty
+        );
         match self_ty.sty {
             ty::TyInfer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
             _ => None,
         }
     }
+
+    fn sig_of_closure(
+        &self,
+        expr_def_id: DefId,
+        decl: &hir::FnDecl,
+        body: &hir::Body,
+        expected_sig: Option<ty::FnSig<'tcx>>,
+    ) -> ClosureSignatures<'tcx> {
+        if let Some(e) = expected_sig {
+            self.sig_of_closure_with_expectation(expr_def_id, decl, body, e)
+        } else {
+            self.sig_of_closure_no_expectation(expr_def_id, decl, body)
+        }
+    }
+
+    /// If there is no expected signature, then we will convert the
+    /// types that the user gave into a signature.
+    fn sig_of_closure_no_expectation(
+        &self,
+        expr_def_id: DefId,
+        decl: &hir::FnDecl,
+        body: &hir::Body,
+    ) -> ClosureSignatures<'tcx> {
+        debug!("sig_of_closure_no_expectation()");
+
+        let bound_sig = self.supplied_sig_of_closure(decl);
+
+        self.closure_sigs(expr_def_id, body, bound_sig)
+    }
+
+    /// Invoked to compute the signature of a closure expression. This
+    /// combines any user-provided type annotations (e.g., `|x: u32|
+    /// -> u32 { .. }`) with the expected signature.
+    ///
+    /// The approach is as follows:
+    ///
+    /// - Let `S` be the (higher-ranked) signature that we derive from the user's annotations.
+    /// - Let `E` be the (higher-ranked) signature that we derive from the expectations, if any.
+    ///   - If we have no expectation `E`, then the signature of the closure is `S`.
+    ///   - Otherwise, the signature of the closure is E. Moreover:
+    ///     - Skolemize the late-bound regions in `E`, yielding `E'`.
+    ///     - Instantiate all the late-bound regions bound in the closure within `S`
+    ///       with fresh (existential) variables, yielding `S'`
+    ///     - Require that `E' = S'`
+    ///       - We could use some kind of subtyping relationship here,
+    ///         I imagine, but equality is easier and works fine for
+    ///         our purposes.
+    ///
+    /// The key intuition here is that the user's types must be valid
+    /// from "the inside" of the closure, but the expectation
+    /// ultimately drives the overall signature.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// fn with_closure<F>(_: F)
+    ///   where F: Fn(&u32) -> &u32 { .. }
+    ///
+    /// with_closure(|x: &u32| { ... })
+    /// ```
+    ///
+    /// Here:
+    /// - E would be `fn(&u32) -> &u32`.
+    /// - S would be `fn(&u32) ->
+    /// - E' is `&'!0 u32 -> &'!0 u32`
+    /// - S' is `&'?0 u32 -> ?T`
+    ///
+    /// S' can be unified with E' with `['?0 = '!0, ?T = &'!10 u32]`.
+    ///
+    /// # Arguments
+    ///
+    /// - `expr_def_id`: the def-id of the closure expression
+    /// - `decl`: the HIR declaration of the closure
+    /// - `body`: the body of the closure
+    /// - `expected_sig`: the expected signature (if any). Note that
+    ///   this is missing a binder: that is, there may be late-bound
+    ///   regions with depth 1, which are bound then by the closure.
+    fn sig_of_closure_with_expectation(
+        &self,
+        expr_def_id: DefId,
+        decl: &hir::FnDecl,
+        body: &hir::Body,
+        expected_sig: ty::FnSig<'tcx>,
+    ) -> ClosureSignatures<'tcx> {
+        debug!(
+            "sig_of_closure_with_expectation(expected_sig={:?})",
+            expected_sig
+        );
+
+        // Watch out for some surprises and just ignore the
+        // expectation if things don't see to match up with what we
+        // expect.
+        if expected_sig.variadic != decl.variadic {
+            return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
+        } else if expected_sig.inputs_and_output.len() != decl.inputs.len() + 1 {
+            // we could probably handle this case more gracefully
+            return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
+        }
+
+        // Create a `PolyFnSig`. Note the oddity that late bound
+        // regions appearing free in `expected_sig` are now bound up
+        // in this binder we are creating.
+        assert!(!expected_sig.has_regions_escaping_depth(1));
+        let bound_sig = ty::Binder(self.tcx.mk_fn_sig(
+            expected_sig.inputs().iter().cloned(),
+            expected_sig.output(),
+            decl.variadic,
+            hir::Unsafety::Normal,
+            Abi::RustCall,
+        ));
+
+        // `deduce_expectations_from_expected_type` introduces
+        // late-bound lifetimes defined elsewhere, which we now
+        // anonymize away, so as not to confuse the user.
+        let bound_sig = self.tcx.anonymize_late_bound_regions(&bound_sig);
+
+        let closure_sigs = self.closure_sigs(expr_def_id, body, bound_sig);
+
+        // Up till this point, we have ignored the annotations that the user
+        // gave. This function will check that they unify successfully.
+        // Along the way, it also writes out entries for types that the user
+        // wrote into our tables, which are then later used by the privacy
+        // check.
+        match self.check_supplied_sig_against_expectation(decl, &closure_sigs) {
+            Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok),
+            Err(_) => return self.sig_of_closure_no_expectation(expr_def_id, decl, body),
+        }
+
+        closure_sigs
+    }
+
+    /// Enforce the user's types against the expectation.  See
+    /// `sig_of_closure_with_expectation` for details on the overall
+    /// strategy.
+    fn check_supplied_sig_against_expectation(
+        &self,
+        decl: &hir::FnDecl,
+        expected_sigs: &ClosureSignatures<'tcx>,
+    ) -> InferResult<'tcx, ()> {
+        // Get the signature S that the user gave.
+        //
+        // (See comment on `sig_of_closure_with_expectation` for the
+        // meaning of these letters.)
+        let supplied_sig = self.supplied_sig_of_closure(decl);
+
+        debug!(
+            "check_supplied_sig_against_expectation: supplied_sig={:?}",
+            supplied_sig
+        );
+
+        // FIXME(#45727): As discussed in [this comment][c1], naively
+        // forcing equality here actually results in suboptimal error
+        // messages in some cases.  For now, if there would have been
+        // an obvious error, we fallback to declaring the type of the
+        // closure to be the one the user gave, which allows other
+        // error message code to trigger.
+        //
+        // However, I think [there is potential to do even better
+        // here][c2], since in *this* code we have the precise span of
+        // the type parameter in question in hand when we report the
+        // error.
+        //
+        // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706
+        // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796
+        self.infcx.commit_if_ok(|_| {
+            let mut all_obligations = vec![];
+
+            // The liberated version of this signature should be be a subtype
+            // of the liberated form of the expectation.
+            for ((hir_ty, &supplied_ty), expected_ty) in decl.inputs.iter()
+                           .zip(*supplied_sig.inputs().skip_binder()) // binder moved to (*) below
+                           .zip(expected_sigs.liberated_sig.inputs())
+            // `liberated_sig` is E'.
+            {
+                // Instantiate (this part of..) S to S', i.e., with fresh variables.
+                let (supplied_ty, _) = self.infcx.replace_late_bound_regions_with_fresh_var(
+                    hir_ty.span,
+                    LateBoundRegionConversionTime::FnCall,
+                    &ty::Binder(supplied_ty),
+                ); // recreated from (*) above
+
+                // Check that E' = S'.
+                let cause = &self.misc(hir_ty.span);
+                let InferOk {
+                    value: (),
+                    obligations,
+                } = self.at(cause, self.param_env)
+                    .eq(*expected_ty, supplied_ty)?;
+                all_obligations.extend(obligations);
+            }
+
+            let (supplied_output_ty, _) = self.infcx.replace_late_bound_regions_with_fresh_var(
+                decl.output.span(),
+                LateBoundRegionConversionTime::FnCall,
+                &supplied_sig.output(),
+            );
+            let cause = &self.misc(decl.output.span());
+            let InferOk {
+                value: (),
+                obligations,
+            } = self.at(cause, self.param_env)
+                .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?;
+            all_obligations.extend(obligations);
+
+            Ok(InferOk {
+                value: (),
+                obligations: all_obligations,
+            })
+        })
+    }
+
+    /// If there is no expected signature, then we will convert the
+    /// types that the user gave into a signature.
+    fn supplied_sig_of_closure(&self, decl: &hir::FnDecl) -> ty::PolyFnSig<'tcx> {
+        let astconv: &AstConv = self;
+
+        // First, convert the types that the user supplied (if any).
+        let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a));
+        let supplied_return = match decl.output {
+            hir::Return(ref output) => astconv.ast_ty_to_ty(&output),
+            hir::DefaultReturn(_) => astconv.ty_infer(decl.output.span()),
+        };
+
+        let result = ty::Binder(self.tcx.mk_fn_sig(
+            supplied_arguments,
+            supplied_return,
+            decl.variadic,
+            hir::Unsafety::Normal,
+            Abi::RustCall,
+        ));
+
+        debug!("supplied_sig_of_closure: result={:?}", result);
+
+        result
+    }
+
+    fn closure_sigs(
+        &self,
+        expr_def_id: DefId,
+        body: &hir::Body,
+        bound_sig: ty::PolyFnSig<'tcx>,
+    ) -> ClosureSignatures<'tcx> {
+        let liberated_sig = self.liberate_late_bound_regions(expr_def_id, &bound_sig);
+        let liberated_sig = self.inh.normalize_associated_types_in(
+            body.value.span,
+            body.value.id,
+            self.param_env,
+            &liberated_sig,
+        );
+        ClosureSignatures {
+            bound_sig,
+            liberated_sig,
+        }
+    }
 }
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 49ba0499f78..5227955d7b9 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -76,6 +76,7 @@ This API is completely unstable and subject to change.
 #![feature(box_patterns)]
 #![feature(box_syntax)]
 #![feature(conservative_impl_trait)]
+#![feature(match_default_bindings)]
 #![feature(never_type)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
diff --git a/src/test/compile-fail/closure-expected-type/README.md b/src/test/compile-fail/closure-expected-type/README.md
new file mode 100644
index 00000000000..9995b00a9a7
--- /dev/null
+++ b/src/test/compile-fail/closure-expected-type/README.md
@@ -0,0 +1 @@
+See `src/test/run-pass/closure-expected-type`.
diff --git a/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn-multiple.rs b/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn-multiple.rs
new file mode 100644
index 00000000000..f1b198a0591
--- /dev/null
+++ b/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn-multiple.rs
@@ -0,0 +1,49 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// must-compile-successfully
+
+#![feature(underscore_lifetimes)]
+#![allow(warnings)]
+
+type Different<'a, 'b> = &'a mut (&'a (), &'b ());
+type Same<'a> = Different<'a, 'a>;
+
+fn with_closure_expecting_different<F>(_: F)
+    where F: for<'a, 'b> FnOnce(Different<'a, 'b>)
+{
+}
+
+fn with_closure_expecting_different_anon<F>(_: F)
+    where F: FnOnce(Different<'_, '_>)
+{
+}
+
+fn supplying_nothing_expecting_anon() {
+    with_closure_expecting_different_anon(|x: Different| {
+    })
+}
+
+fn supplying_nothing_expecting_named() {
+    with_closure_expecting_different(|x: Different| {
+    })
+}
+
+fn supplying_underscore_expecting_anon() {
+    with_closure_expecting_different_anon(|x: Different<'_, '_>| {
+    })
+}
+
+fn supplying_underscore_expecting_named() {
+    with_closure_expecting_different(|x: Different<'_, '_>| {
+    })
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn.rs b/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn.rs
new file mode 100644
index 00000000000..645fd1f80ba
--- /dev/null
+++ b/src/test/compile-fail/closure-expected-type/expect-fn-supply-fn.rs
@@ -0,0 +1,70 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(underscore_lifetimes)]
+
+fn with_closure_expecting_fn_with_free_region<F>(_: F)
+    where F: for<'a> FnOnce(fn(&'a u32), &i32)
+{
+}
+
+fn with_closure_expecting_fn_with_bound_region<F>(_: F)
+    where F: FnOnce(fn(&u32), &i32)
+{
+}
+
+fn expect_free_supply_free_from_fn<'x>(x: &'x u32) {
+    // Here, the type given for `'x` "obscures" a region from the
+    // expected signature that is bound at closure level.
+    with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {});
+    //~^ ERROR mismatched types
+    //~| ERROR mismatched types
+}
+
+fn expect_free_supply_free_from_closure() {
+    // A variant on the previous test. Here, the region `'a` will be
+    // bound at the closure level, just as is expected, so no error
+    // results.
+    type Foo<'a> = fn(&'a u32);
+    with_closure_expecting_fn_with_free_region(|_x: Foo<'_>, y| {});
+}
+
+fn expect_free_supply_bound() {
+    // Here, we are given a function whose region is bound at closure level,
+    // but we expect one bound in the argument. Error results.
+    with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
+    //~^ ERROR type mismatch in closure arguments
+}
+
+fn expect_bound_supply_free_from_fn<'x>(x: &'x u32) {
+    // Here, we are given a `fn(&u32)` but we expect a `fn(&'x
+    // u32)`. In principle, this could be ok, but we demand equality.
+    with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
+    //~^ ERROR type mismatch in closure arguments
+}
+
+fn expect_bound_supply_free_from_closure() {
+    // A variant on the previous test. Here, the region `'a` will be
+    // bound at the closure level, but we expect something bound at
+    // the argument level.
+    type Foo<'a> = fn(&'a u32);
+    with_closure_expecting_fn_with_bound_region(|_x: Foo<'_>, y| {});
+    //~^ ERROR type mismatch in closure arguments
+}
+
+fn expect_bound_supply_bound<'x>(x: &'x u32) {
+    // No error in this case. The supplied type supplies the bound
+    // regions, and hence we are able to figure out the type of `y`
+    // from the expected type
+    with_closure_expecting_fn_with_bound_region(|x: for<'z> fn(&'z u32), y| {
+    });
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/closure-expected-type/expect-infer-var-appearing-twice.rs b/src/test/compile-fail/closure-expected-type/expect-infer-var-appearing-twice.rs
new file mode 100644
index 00000000000..bef69a4b0b9
--- /dev/null
+++ b/src/test/compile-fail/closure-expected-type/expect-infer-var-appearing-twice.rs
@@ -0,0 +1,35 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn with_closure<F, A>(_: F)
+    where F: FnOnce(A, A)
+{
+}
+
+fn a() {
+    with_closure(|x: u32, y| {
+        // We deduce type of `y` from `x`.
+    });
+}
+
+fn b() {
+    // Here we take the supplied types, resulting in an error later on.
+    with_closure(|x: u32, y: i32| {
+        //~^ ERROR type mismatch in closure arguments
+    });
+}
+
+fn c() {
+    with_closure(|x, y: i32| {
+        // We deduce type of `x` from `y`.
+    });
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-bound-region.rs b/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-bound-region.rs
new file mode 100644
index 00000000000..f8cb643c8d6
--- /dev/null
+++ b/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-bound-region.rs
@@ -0,0 +1,29 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// must-compile-successfully
+
+fn with_closure<F, A>(_: F)
+    where F: FnOnce(A, &u32)
+{
+}
+
+fn foo() {
+    // This version works; we infer `A` to be `u32`, and take the type
+    // of `y` to be `&u32`.
+    with_closure(|x: u32, y| {});
+}
+
+fn bar() {
+    // This version also works.
+    with_closure(|x: &u32, y| {});
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-free-region.rs b/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-free-region.rs
new file mode 100644
index 00000000000..d3c111c5daf
--- /dev/null
+++ b/src/test/compile-fail/closure-expected-type/expect-infer-var-supply-ty-with-free-region.rs
@@ -0,0 +1,29 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// must-compile-successfully
+
+fn with_closure<F, A>(_: F)
+    where F: FnOnce(A, &u32)
+{
+}
+
+fn foo() {
+    // This version works; we infer `A` to be `u32`, and take the type
+    // of `y` to be `&u32`.
+    with_closure(|x: u32, y| {});
+}
+
+fn bar<'x>(x: &'x u32) {
+    // Same.
+    with_closure(|x: &'x u32, y| {});
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs b/src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs
new file mode 100644
index 00000000000..9da12dc901f
--- /dev/null
+++ b/src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs
@@ -0,0 +1,80 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(warnings)]
+
+fn closure_expecting_bound<F>(_: F)
+    where F: FnOnce(&u32)
+{
+}
+
+fn closure_expecting_free<'a, F>(_: F)
+    where F: FnOnce(&'a u32)
+{
+}
+
+fn expect_bound_supply_nothing() {
+    // Because `x` is inferred to have a bound region, we cannot allow
+    // it to escape into `f`:
+    let mut f: Option<&u32> = None;
+    closure_expecting_bound(|x| {
+        f = Some(x); //~ ERROR E0495
+    });
+}
+
+fn expect_bound_supply_bound() {
+    // Because `x` is inferred to have a bound region, we cannot allow
+    // it to escape into `f`, even with an explicit type annotation on
+    // closure:
+    let mut f: Option<&u32> = None;
+    closure_expecting_bound(|x: &u32| {
+        f = Some(x); //~ ERROR E0495
+    });
+}
+
+fn expect_bound_supply_named<'x>() {
+    let mut f: Option<&u32> = None;
+
+    // Here we give a type annotation that `x` should be free. We get
+    // an error because of that.
+    closure_expecting_bound(|x: &'x u32| {
+        //~^ ERROR mismatched types
+        //~| ERROR mismatched types
+
+        // And we still cannot let `x` escape into `f`.
+        f = Some(x);
+        //~^ ERROR cannot infer
+    });
+}
+
+fn expect_free_supply_nothing() {
+    let mut f: Option<&u32> = None;
+    closure_expecting_free(|x| f = Some(x)); // OK
+}
+
+fn expect_free_supply_bound() {
+    let mut f: Option<&u32> = None;
+
+    // Here, even though the annotation `&u32` could be seen as being
+    // bound in the closure, we permit it to be defined as a free
+    // region (which is inferred to something in the fn body).
+    closure_expecting_free(|x: &u32| f = Some(x)); // OK
+}
+
+fn expect_free_supply_named<'x>() {
+    let mut f: Option<&u32> = None;
+
+    // Here, even though the annotation `&u32` could be seen as being
+    // bound in the closure, we permit it to be defined as a free
+    // region (which is inferred to something in the fn body).
+    closure_expecting_free(|x: &'x u32| f = Some(x)); // OK
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.rs b/src/test/compile-fail/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.rs
new file mode 100644
index 00000000000..377eaadbd6a
--- /dev/null
+++ b/src/test/compile-fail/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.rs
@@ -0,0 +1,29 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn with_closure<F, A, B>(_: F)
+    where F: FnOnce(A, B)
+{
+}
+
+fn a() {
+    // Type of `y` is unconstrained.
+    with_closure(|x: u32, y| {}); //~ ERROR E0282
+}
+
+fn b() {
+    with_closure(|x: u32, y: u32| {}); // OK
+}
+
+fn c() {
+    with_closure(|x: u32, y: u32| {}); // OK
+}
+
+fn main() { }
diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs
index 53454c0cc9a..a97ce4e8cbe 100644
--- a/src/test/mir-opt/validate_1.rs
+++ b/src/test/mir-opt/validate_1.rs
@@ -62,7 +62,7 @@ fn main() {
 // fn main::{{closure}}(_1: &ReErased [closure@NodeId(50)], _2: &ReErased mut i32) -> i32 {
 //     ...
 //     bb0: {
-//         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[317d]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(50)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[317d]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]);
+//         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[317d]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(50)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[317d]::main[0]::{{closure}}[0] }, BrAnon(0)) mut i32]);
 //         StorageLive(_3);
 //         Validate(Suspend(ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 }))), [(*_2): i32]);
 //         _3 = &ReErased (*_2);
diff --git a/src/test/mir-opt/validate_4.rs b/src/test/mir-opt/validate_4.rs
index 042edca82a6..3585ac0b8be 100644
--- a/src/test/mir-opt/validate_4.rs
+++ b/src/test/mir-opt/validate_4.rs
@@ -78,8 +78,8 @@ fn main() {
 // fn main::{{closure}}(_1: &ReErased [closure@NodeId(60)], _2: &ReErased mut i32) -> bool {
 //     ...
 //     bb0: {
-//         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[317d]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[317d]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]);
-//         Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[317d]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[317d]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]);
+//         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[317d]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[317d]::main[0]::{{closure}}[0] }, BrAnon(0)) mut i32]);
+//         Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[317d]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[317d]::main[0]::{{closure}}[0] }, BrAnon(0)) mut i32]);
 //         StorageLive(_3);
 //         ...
 //         _0 = const write_42(_3) -> bb1;
diff --git a/src/test/mir-opt/validate_5.rs b/src/test/mir-opt/validate_5.rs
index fc849c5aee3..ae09d72942e 100644
--- a/src/test/mir-opt/validate_5.rs
+++ b/src/test/mir-opt/validate_5.rs
@@ -49,7 +49,7 @@ fn main() {
 // fn main::{{closure}}(_1: &ReErased [closure@NodeId(46)], _2: &ReErased mut i32) -> bool {
 //     ...
 //     bb0: {
-//         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[317d]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(46)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[317d]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]);
+//         Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[317d]::main[0]::{{closure}}[0] }, BrEnv) [closure@NodeId(46)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[317d]::main[0]::{{closure}}[0] }, BrAnon(0)) mut i32]);
 //         StorageLive(_3);
 //         StorageLive(_4);
 //         Validate(Suspend(ReScope(Node(ItemLocalId(9)))), [(*_2): i32]);
diff --git a/src/test/run-pass/closure-expected-type/README.md b/src/test/run-pass/closure-expected-type/README.md
new file mode 100644
index 00000000000..fd493e1ff37
--- /dev/null
+++ b/src/test/run-pass/closure-expected-type/README.md
@@ -0,0 +1,8 @@
+Some tests targeted at how we deduce the types of closure arguments.
+This process is a result of some heuristics aimed at combining the
+*expected type* we have with the *actual types* that we get from
+inputs. This investigation was kicked off by #38714, which revealed
+some pretty deep flaws in the ad-hoc way that we were doing things
+before.
+
+See also `src/test/compile-fail/closure-expected-type`.
diff --git a/src/test/run-pass/closure-expected-type/expect-infer-supply-two-infers.rs b/src/test/run-pass/closure-expected-type/expect-infer-supply-two-infers.rs
new file mode 100644
index 00000000000..8a90a491f7e
--- /dev/null
+++ b/src/test/run-pass/closure-expected-type/expect-infer-supply-two-infers.rs
@@ -0,0 +1,26 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn with_closure<A, F>(_: F)
+    where F: FnOnce(Vec<A>, A)
+{
+}
+
+fn expect_free_supply_free<'x>(x: &'x u32) {
+    with_closure(|mut x: Vec<_>, y| {
+        // Shows that the call to `x.push()` is influencing type of `y`...
+        x.push(22_u32);
+
+        // ...since we now know the type of `y` and can resolve the method call.
+        y.wrapping_add(1);
+    });
+}
+
+fn main() { }
diff --git a/src/test/run-pass/closure-expected-type/issue-38714.rs b/src/test/run-pass/closure-expected-type/issue-38714.rs
new file mode 100644
index 00000000000..a1d512105c9
--- /dev/null
+++ b/src/test/run-pass/closure-expected-type/issue-38714.rs
@@ -0,0 +1,26 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct UsizeRef<'a> {
+    a: &'a usize
+}
+
+type RefTo = Box<for<'r> Fn(&'r Vec<usize>) -> UsizeRef<'r>>;
+
+fn ref_to<'a>(vec: &'a Vec<usize>) -> UsizeRef<'a> {
+    UsizeRef{ a: &vec[0]}
+}
+
+fn main() {
+    // Regression test: this was causing ICEs; it should compile.
+    let a: RefTo = Box::new(|vec: &Vec<usize>| {
+        UsizeRef{ a: &vec[0] }
+    });
+}
diff --git a/src/test/run-pass/closure-expected-type/supply-just-return-type.rs b/src/test/run-pass/closure-expected-type/supply-just-return-type.rs
new file mode 100644
index 00000000000..0b930b338fd
--- /dev/null
+++ b/src/test/run-pass/closure-expected-type/supply-just-return-type.rs
@@ -0,0 +1,35 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn with_closure<F, R>(f: F) -> Result<char, R>
+    where F: FnOnce(&char) -> Result<char, R>,
+{
+    f(&'a')
+}
+
+fn main() {
+    // Test that supplying the `-> Result<char, ()>` manually here
+    // (which is needed to constrain `R`) still allows us to figure
+    // out that the type of `x` is `&'a char` where `'a` is bound in
+    // the closure (if we didn't, we'd get a type-error because
+    // `with_closure` requires a bound region).
+    //
+    // This pattern was found in the wild.
+    let z = with_closure(|x| -> Result<char, ()> { Ok(*x) });
+    assert_eq!(z.unwrap(), 'a');
+
+    // It also works with `_`:
+    let z = with_closure(|x: _| -> Result<char, ()> { Ok(*x) });
+    assert_eq!(z.unwrap(), 'a');
+
+    // It also works with `&_`:
+    let z = with_closure(|x: &_| -> Result<char, ()> { Ok(*x) });
+    assert_eq!(z.unwrap(), 'a');
+}
diff --git a/src/test/run-pass/closure-expected-type/supply-nothing.rs b/src/test/run-pass/closure-expected-type/supply-nothing.rs
new file mode 100644
index 00000000000..15d8b393c15
--- /dev/null
+++ b/src/test/run-pass/closure-expected-type/supply-nothing.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn with_closure<F>(f: F) -> u32
+    where F: FnOnce(&u32, &u32) -> u32
+{
+    f(&22, &44)
+}
+
+fn main() {
+    let z = with_closure(|x, y| x + y).wrapping_add(1);
+    assert_eq!(z, 22 + 44 + 1);
+}
diff --git a/src/test/ui/mismatched_types/closure-arg-count.stderr b/src/test/ui/mismatched_types/closure-arg-count.stderr
index ca06825d0ad..9de5e8fea01 100644
--- a/src/test/ui/mismatched_types/closure-arg-count.stderr
+++ b/src/test/ui/mismatched_types/closure-arg-count.stderr
@@ -14,23 +14,6 @@ error[E0593]: closure is expected to take 2 arguments, but it takes 1 argument
    |               |
    |               expected closure that takes 2 arguments
 
-error: non-reference pattern used to match a reference (see issue #42640)
-  --> $DIR/closure-arg-count.rs:17:24
-   |
-17 |     [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
-   |                        ^^^^^^^^^^^^^^^ help: consider using: `&(tuple, tuple2)`
-   |
-   = help: add #![feature(match_default_bindings)] to the crate attributes to enable
-
-error[E0308]: mismatched types
-  --> $DIR/closure-arg-count.rs:17:24
-   |
-17 |     [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
-   |                        ^^^^^^^^^^^^^^^ expected integral variable, found tuple
-   |
-   = note: expected type `{integer}`
-              found type `(_, _)`
-
 error[E0593]: closure is expected to take 2 arguments, but it takes 1 argument
   --> $DIR/closure-arg-count.rs:17:15
    |
@@ -73,5 +56,5 @@ error[E0593]: closure is expected to take a single 2-tuple as argument, but it t
    |                                                     |
    |                                                     expected closure that takes a single 2-tuple as argument
 
-error: aborting due to 9 previous errors
+error: aborting due to 7 previous errors