about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-01-04 11:51:30 +0000
committerbors <bors@rust-lang.org>2019-01-04 11:51:30 +0000
commita602f13f02acc17eda0d7f2c2d9adeabbdb59cf4 (patch)
tree04a0bd2d05491d2c0c03a4a353247a8cf3d67191 /src
parentae38baed33de726cbbf5edde75a3ae1dd311ebc9 (diff)
parent91c155bcd7af54fba0b705de1a2c2d55fa9788fa (diff)
downloadrust-a602f13f02acc17eda0d7f2c2d9adeabbdb59cf4.tar.gz
rust-a602f13f02acc17eda0d7f2c2d9adeabbdb59cf4.zip
Auto merge of #55986 - cjgillot:issue-45510, r=nikomatsakis
Allow to dispatch fn traits depending on number of parameters

Hello,

By following @eddyb's advise on issue #45510, I managed to have the snippets of code in #45510 and #18952 passing without breaking older diagnostics.

EDIT: the codegen tests breakage I experienced is due to the poor quality of my laptop.

If any kind reviewer has any advice, you are very welcome.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/callee.rs366
-rw-r--r--src/test/run-pass/issue-18952.rs56
-rw-r--r--src/test/run-pass/issue-45510.rs32
3 files changed, 309 insertions, 145 deletions
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index d4f639c070d..c59c143f74b 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -1,16 +1,17 @@
-use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
 use super::autoderef::Autoderef;
 use super::method::MethodCallee;
+use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
 
+use errors::Applicability;
 use hir::def::Def;
 use hir::def_id::{DefId, LOCAL_CRATE};
+use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
+use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc::{infer, traits};
-use rustc::ty::{self, TyCtxt, TypeFoldable, Ty};
-use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
+use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc_target::spec::abi;
 use syntax::ast::Ident;
 use syntax_pos::Span;
-use errors::Applicability;
 
 use rustc::hir;
 
@@ -33,19 +34,20 @@ enum CallStep<'tcx> {
 }
 
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
-    pub fn check_call(&self,
-                      call_expr: &'gcx hir::Expr,
-                      callee_expr: &'gcx hir::Expr,
-                      arg_exprs: &'gcx [hir::Expr],
-                      expected: Expectation<'tcx>)
-                      -> Ty<'tcx> {
+    pub fn check_call(
+        &self,
+        call_expr: &'gcx hir::Expr,
+        callee_expr: &'gcx hir::Expr,
+        arg_exprs: &'gcx [hir::Expr],
+        expected: Expectation<'tcx>,
+    ) -> Ty<'tcx> {
         let original_callee_ty = self.check_expr(callee_expr);
         let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty);
 
         let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
         let mut result = None;
         while result.is_none() && autoderef.next().is_some() {
-            result = self.try_overloaded_call_step(call_expr, callee_expr, &autoderef);
+            result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef);
         }
         autoderef.finalize(self);
 
@@ -74,15 +76,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         output
     }
 
-    fn try_overloaded_call_step(&self,
-                                call_expr: &'gcx hir::Expr,
-                                callee_expr: &'gcx hir::Expr,
-                                autoderef: &Autoderef<'a, 'gcx, 'tcx>)
-                                -> Option<CallStep<'tcx>> {
+    fn try_overloaded_call_step(
+        &self,
+        call_expr: &'gcx hir::Expr,
+        callee_expr: &'gcx hir::Expr,
+        arg_exprs: &'gcx [hir::Expr],
+        autoderef: &Autoderef<'a, 'gcx, 'tcx>,
+    ) -> Option<CallStep<'tcx>> {
         let adjusted_ty = autoderef.unambiguous_final_ty(self);
-        debug!("try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})",
-               call_expr,
-               adjusted_ty);
+        debug!(
+            "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})",
+            call_expr, adjusted_ty
+        );
 
         // If the callee is a bare function or a closure, then we're all set.
         match adjusted_ty.sty {
@@ -100,21 +105,26 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // fnmut vs fnonce. If so, we have to defer further processing.
                 if self.closure_kind(def_id, substs).is_none() {
                     let closure_ty = self.closure_sig(def_id, substs);
-                    let fn_sig = self.replace_bound_vars_with_fresh_vars(
-                        call_expr.span,
-                        infer::FnCall,
-                        &closure_ty
-                    ).0;
+                    let fn_sig = self
+                        .replace_bound_vars_with_fresh_vars(
+                            call_expr.span,
+                            infer::FnCall,
+                            &closure_ty,
+                        )
+                        .0;
                     let adjustments = autoderef.adjust_steps(self, Needs::None);
-                    self.record_deferred_call_resolution(def_id, DeferredCallResolution {
-                        call_expr,
-                        callee_expr,
-                        adjusted_ty,
-                        adjustments,
-                        fn_sig,
-                        closure_def_id: def_id,
-                        closure_substs: substs,
-                    });
+                    self.record_deferred_call_resolution(
+                        def_id,
+                        DeferredCallResolution {
+                            call_expr,
+                            callee_expr,
+                            adjusted_ty,
+                            adjustments,
+                            fn_sig,
+                            closure_def_id: def_id,
+                            closure_substs: substs,
+                        },
+                    );
                     return Some(CallStep::DeferredClosure(fn_sig));
                 }
             }
@@ -134,34 +144,68 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             _ => {}
         }
 
-        self.try_overloaded_call_traits(call_expr, adjusted_ty).map(|(autoref, method)| {
-            let mut adjustments = autoderef.adjust_steps(self, Needs::None);
-            adjustments.extend(autoref);
-            self.apply_adjustments(callee_expr, adjustments);
-            CallStep::Overloaded(method)
-        })
+        // Now, we look for the implementation of a Fn trait on the object's type.
+        // We first do it with the explicit instruction to look for an impl of
+        // `Fn<Tuple>`, with the tuple `Tuple` having an arity corresponding
+        // to the number of call parameters.
+        // If that fails (or_else branch), we try again without specifying the
+        // shape of the tuple (hence the None). This allows to detect an Fn trait
+        // is implemented, and use this information for diagnostic.
+        self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs))
+            .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None))
+            .map(|(autoref, method)| {
+                let mut adjustments = autoderef.adjust_steps(self, Needs::None);
+                adjustments.extend(autoref);
+                self.apply_adjustments(callee_expr, adjustments);
+                CallStep::Overloaded(method)
+            })
     }
 
-    fn try_overloaded_call_traits(&self,
-                                  call_expr: &hir::Expr,
-                                  adjusted_ty: Ty<'tcx>)
-                                  -> Option<(Option<Adjustment<'tcx>>,
-                                             MethodCallee<'tcx>)> {
+    fn try_overloaded_call_traits(
+        &self,
+        call_expr: &hir::Expr,
+        adjusted_ty: Ty<'tcx>,
+        opt_arg_exprs: Option<&'gcx [hir::Expr]>,
+    ) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
         // Try the options that are least restrictive on the caller first.
-        for &(opt_trait_def_id, method_name, borrow) in
-            &[(self.tcx.lang_items().fn_trait(), Ident::from_str("call"), true),
-              (self.tcx.lang_items().fn_mut_trait(), Ident::from_str("call_mut"), true),
-              (self.tcx.lang_items().fn_once_trait(), Ident::from_str("call_once"), false)] {
+        for &(opt_trait_def_id, method_name, borrow) in &[
+            (
+                self.tcx.lang_items().fn_trait(),
+                Ident::from_str("call"),
+                true,
+            ),
+            (
+                self.tcx.lang_items().fn_mut_trait(),
+                Ident::from_str("call_mut"),
+                true,
+            ),
+            (
+                self.tcx.lang_items().fn_once_trait(),
+                Ident::from_str("call_once"),
+                false,
+            ),
+        ] {
             let trait_def_id = match opt_trait_def_id {
                 Some(def_id) => def_id,
                 None => continue,
             };
 
-            if let Some(ok) = self.lookup_method_in_trait(call_expr.span,
-                                                          method_name,
-                                                          trait_def_id,
-                                                          adjusted_ty,
-                                                          None) {
+            let opt_input_types = opt_arg_exprs.map(|arg_exprs| [self.tcx.mk_tup(
+                arg_exprs
+                .iter()
+                .map(|e| self.next_ty_var(
+                    TypeVariableOrigin::TypeInference(e.span)
+                ))
+            )]);
+            let opt_input_types = opt_input_types.as_ref().map(AsRef::as_ref);
+
+            if let Some(ok) = self.lookup_method_in_trait(
+                call_expr.span,
+                method_name,
+                trait_def_id,
+                adjusted_ty,
+                opt_input_types,
+            ) {
                 let method = self.register_infer_ok_obligations(ok);
                 let mut autoref = None;
                 if borrow {
@@ -173,11 +217,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                 // deployment, conservatively omit
                                 // overloaded function call ops.
                                 allow_two_phase_borrow: AllowTwoPhase::No,
-                            }
+                            },
                         };
                         autoref = Some(Adjustment {
                             kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
-                            target: method.sig.inputs()[0]
+                            target: method.sig.inputs()[0],
                         });
                     }
                 }
@@ -188,16 +232,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         None
     }
 
-    fn confirm_builtin_call(&self,
-                            call_expr: &hir::Expr,
-                            callee_ty: Ty<'tcx>,
-                            arg_exprs: &'gcx [hir::Expr],
-                            expected: Expectation<'tcx>)
-                            -> Ty<'tcx> {
+    fn confirm_builtin_call(
+        &self,
+        call_expr: &hir::Expr,
+        callee_ty: Ty<'tcx>,
+        arg_exprs: &'gcx [hir::Expr],
+        expected: Expectation<'tcx>,
+    ) -> Ty<'tcx> {
         let (fn_sig, def_span) = match callee_ty.sty {
-            ty::FnDef(def_id, _) => {
-                (callee_ty.fn_sig(self.tcx), self.tcx.hir().span_if_local(def_id))
-            }
+            ty::FnDef(def_id, _) => (
+                callee_ty.fn_sig(self.tcx),
+                self.tcx.hir().span_if_local(def_id),
+            ),
             ty::FnPtr(sig) => (sig, None),
             ref t => {
                 let mut unit_variant = None;
@@ -219,15 +265,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         match unit_variant {
                             Some(ref path) => format!("enum variant `{}`", path),
                             None => format!("`{}`", callee_ty),
-                        });
+                        }
+                    );
 
                     if let Some(ref path) = unit_variant {
                         err.span_suggestion_with_applicability(
                             call_expr.span,
-                            &format!("`{}` is a unit variant, you need to write it \
-                                      without the parenthesis", path),
+                            &format!(
+                                "`{}` is a unit variant, you need to write it \
+                                 without the parenthesis",
+                                path
+                            ),
                             path.to_string(),
-                            Applicability::MachineApplicable
+                            Applicability::MachineApplicable,
                         );
                     }
 
@@ -235,48 +285,50 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     let def = match callee.node {
                         hir::ExprKind::Path(ref qpath) => {
                             self.tables.borrow().qpath_def(qpath, callee.hir_id)
-                        },
+                        }
                         hir::ExprKind::Call(ref inner_callee, _) => {
                             // If the call spans more than one line and the callee kind is
                             // itself another `ExprCall`, that's a clue that we might just be
                             // missing a semicolon (Issue #51055)
-                            let call_is_multiline = self.tcx.sess.source_map()
-                                .is_multiline(call_expr.span);
+                            let call_is_multiline =
+                                self.tcx.sess.source_map().is_multiline(call_expr.span);
                             if call_is_multiline {
                                 let span = self.tcx.sess.source_map().next_point(callee.span);
                                 err.span_suggestion_with_applicability(
                                     span,
                                     "try adding a semicolon",
                                     ";".to_owned(),
-                                    Applicability::MaybeIncorrect
+                                    Applicability::MaybeIncorrect,
                                 );
                             }
                             if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.node {
                                 inner_callee_path = Some(inner_qpath);
-                                self.tables.borrow().qpath_def(inner_qpath, inner_callee.hir_id)
+                                self.tables
+                                    .borrow()
+                                    .qpath_def(inner_qpath, inner_callee.hir_id)
                             } else {
                                 Def::Err
                             }
-                        },
-                        _ => {
-                            Def::Err
                         }
+                        _ => Def::Err,
                     };
 
                     err.span_label(call_expr.span, "call expression requires function");
 
                     let def_span = match def {
                         Def::Err => None,
-                        Def::Local(id) | Def::Upvar(id, ..) => {
-                            Some(self.tcx.hir().span(id))
-                        }
-                        _ => def.opt_def_id().and_then(|did| self.tcx.hir().span_if_local(did)),
+                        Def::Local(id) | Def::Upvar(id, ..) => Some(self.tcx.hir().span(id)),
+                        _ => def
+                            .opt_def_id()
+                            .and_then(|did| self.tcx.hir().span_if_local(did)),
                     };
                     if let Some(span) = def_span {
                         let label = match (unit_variant, inner_callee_path) {
                             (Some(path), _) => format!("`{}` defined here", path),
                             (_, Some(hir::QPath::Resolved(_, path))) => format!(
-                                "`{}` defined here returns `{}`", path, callee_ty.to_string()
+                                "`{}` defined here returns `{}`",
+                                path,
+                                callee_ty.to_string()
                             ),
                             _ => format!("`{}` defined here", callee_ty.to_string()),
                         };
@@ -284,19 +336,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     }
                     err.emit();
                 } else {
-                    bug!("call_expr.node should be an ExprKind::Call, got {:?}", call_expr.node);
+                    bug!(
+                        "call_expr.node should be an ExprKind::Call, got {:?}",
+                        call_expr.node
+                    );
                 }
 
                 // This is the "default" function signature, used in case of error.
                 // In that case, we check each argument against "error" in order to
                 // set up all the node type bindings.
-                (ty::Binder::bind(self.tcx.mk_fn_sig(
-                    self.err_args(arg_exprs.len()).into_iter(),
-                    self.tcx.types.err,
-                    false,
-                    hir::Unsafety::Normal,
-                    abi::Abi::Rust
-                )), None)
+                (
+                    ty::Binder::bind(self.tcx.mk_fn_sig(
+                        self.err_args(arg_exprs.len()).into_iter(),
+                        self.tcx.types.err,
+                        false,
+                        hir::Unsafety::Normal,
+                        abi::Abi::Rust,
+                    )),
+                    None,
+                )
             }
         };
 
@@ -305,69 +363,80 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // renormalize the associated types at this point, since they
         // previously appeared within a `Binder<>` and hence would not
         // have been normalized before.
-        let fn_sig =
-            self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, &fn_sig)
-                .0;
+        let fn_sig = self
+            .replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, &fn_sig)
+            .0;
         let fn_sig = self.normalize_associated_types_in(call_expr.span, &fn_sig);
 
         // Call the generic checker.
-        let expected_arg_tys =
-            self.expected_inputs_for_expected_output(call_expr.span,
-                                            expected,
-                                            fn_sig.output(),
-                                            fn_sig.inputs());
-        self.check_argument_types(call_expr.span,
-                                  call_expr.span,
-                                  fn_sig.inputs(),
-                                  &expected_arg_tys[..],
-                                  arg_exprs,
-                                  fn_sig.variadic,
-                                  TupleArgumentsFlag::DontTupleArguments,
-                                  def_span);
+        let expected_arg_tys = self.expected_inputs_for_expected_output(
+            call_expr.span,
+            expected,
+            fn_sig.output(),
+            fn_sig.inputs(),
+        );
+        self.check_argument_types(
+            call_expr.span,
+            call_expr.span,
+            fn_sig.inputs(),
+            &expected_arg_tys[..],
+            arg_exprs,
+            fn_sig.variadic,
+            TupleArgumentsFlag::DontTupleArguments,
+            def_span,
+        );
 
         fn_sig.output()
     }
 
-    fn confirm_deferred_closure_call(&self,
-                                     call_expr: &hir::Expr,
-                                     arg_exprs: &'gcx [hir::Expr],
-                                     expected: Expectation<'tcx>,
-                                     fn_sig: ty::FnSig<'tcx>)
-                                     -> Ty<'tcx> {
+    fn confirm_deferred_closure_call(
+        &self,
+        call_expr: &hir::Expr,
+        arg_exprs: &'gcx [hir::Expr],
+        expected: Expectation<'tcx>,
+        fn_sig: ty::FnSig<'tcx>,
+    ) -> Ty<'tcx> {
         // `fn_sig` is the *signature* of the cosure being called. We
         // don't know the full details yet (`Fn` vs `FnMut` etc), but we
         // do know the types expected for each argument and the return
         // type.
 
-        let expected_arg_tys = self.expected_inputs_for_expected_output(call_expr.span,
-                                                               expected,
-                                                               fn_sig.output().clone(),
-                                                               fn_sig.inputs());
-
-        self.check_argument_types(call_expr.span,
-                                  call_expr.span,
-                                  fn_sig.inputs(),
-                                  &expected_arg_tys,
-                                  arg_exprs,
-                                  fn_sig.variadic,
-                                  TupleArgumentsFlag::TupleArguments,
-                                  None);
+        let expected_arg_tys = self.expected_inputs_for_expected_output(
+            call_expr.span,
+            expected,
+            fn_sig.output().clone(),
+            fn_sig.inputs(),
+        );
+
+        self.check_argument_types(
+            call_expr.span,
+            call_expr.span,
+            fn_sig.inputs(),
+            &expected_arg_tys,
+            arg_exprs,
+            fn_sig.variadic,
+            TupleArgumentsFlag::TupleArguments,
+            None,
+        );
 
         fn_sig.output()
     }
 
-    fn confirm_overloaded_call(&self,
-                               call_expr: &hir::Expr,
-                               arg_exprs: &'gcx [hir::Expr],
-                               expected: Expectation<'tcx>,
-                               method_callee: MethodCallee<'tcx>)
-                               -> Ty<'tcx> {
-        let output_type = self.check_method_argument_types(call_expr.span,
-                                                           call_expr.span,
-                                                           Ok(method_callee),
-                                                           arg_exprs,
-                                                           TupleArgumentsFlag::TupleArguments,
-                                                           expected);
+    fn confirm_overloaded_call(
+        &self,
+        call_expr: &hir::Expr,
+        arg_exprs: &'gcx [hir::Expr],
+        expected: Expectation<'tcx>,
+        method_callee: MethodCallee<'tcx>,
+    ) -> Ty<'tcx> {
+        let output_type = self.check_method_argument_types(
+            call_expr.span,
+            call_expr.span,
+            Ok(method_callee),
+            arg_exprs,
+            TupleArgumentsFlag::TupleArguments,
+            expected,
+        );
 
         self.write_method_call(call_expr.hir_id, method_callee);
         output_type
@@ -391,11 +460,12 @@ impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> {
 
         // we should not be invoked until the closure kind has been
         // determined by upvar inference
-        assert!(fcx.closure_kind(self.closure_def_id, self.closure_substs).is_some());
+        assert!(fcx
+            .closure_kind(self.closure_def_id, self.closure_substs)
+            .is_some());
 
         // We may now know enough to figure out fn vs fnmut etc.
-        match fcx.try_overloaded_call_traits(self.call_expr,
-                                             self.adjusted_ty) {
+        match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) {
             Some((autoref, method_callee)) => {
                 // One problem is that when we get here, we are going
                 // to have a newly instantiated function signature
@@ -410,22 +480,28 @@ impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> {
                 debug!("attempt_resolution: method_callee={:?}", method_callee);
 
                 for (method_arg_ty, self_arg_ty) in
-                    method_sig.inputs().iter().skip(1).zip(self.fn_sig.inputs()) {
+                    method_sig.inputs().iter().skip(1).zip(self.fn_sig.inputs())
+                {
                     fcx.demand_eqtype(self.call_expr.span, &self_arg_ty, &method_arg_ty);
                 }
 
-                fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output());
+                fcx.demand_eqtype(
+                    self.call_expr.span,
+                    method_sig.output(),
+                    self.fn_sig.output(),
+                );
 
                 let mut adjustments = self.adjustments;
                 adjustments.extend(autoref);
                 fcx.apply_adjustments(self.callee_expr, adjustments);
 
-                fcx.write_method_call(self.call_expr.hir_id,
-                                      method_callee);
+                fcx.write_method_call(self.call_expr.hir_id, method_callee);
             }
             None => {
-                span_bug!(self.call_expr.span,
-                          "failed to find an overloaded call trait for closure call");
+                span_bug!(
+                    self.call_expr.span,
+                    "failed to find an overloaded call trait for closure call"
+                );
             }
         }
     }
diff --git a/src/test/run-pass/issue-18952.rs b/src/test/run-pass/issue-18952.rs
new file mode 100644
index 00000000000..56378b59e36
--- /dev/null
+++ b/src/test/run-pass/issue-18952.rs
@@ -0,0 +1,56 @@
+// This issue tests fn_traits overloading on arity.
+// run-pass
+
+#![feature(fn_traits)]
+#![feature(unboxed_closures)]
+
+struct Foo;
+
+impl Fn<(isize, isize)> for Foo {
+    extern "rust-call" fn call(&self, args: (isize, isize)) -> Self::Output {
+        println!("{:?}", args);
+        (args.0 + 1, args.1 + 1)
+    }
+}
+
+impl FnMut<(isize, isize)> for Foo {
+    extern "rust-call" fn call_mut(&mut self, args: (isize, isize)) -> Self::Output {
+        println!("{:?}", args);
+        (args.0 + 1, args.1 + 1)
+    }
+}
+
+impl FnOnce<(isize, isize)> for Foo {
+    type Output = (isize, isize);
+    extern "rust-call" fn call_once(self, args: (isize, isize)) -> Self::Output {
+        println!("{:?}", args);
+        (args.0 + 1, args.1 + 1)
+    }
+}
+
+impl Fn<(isize, isize, isize)> for Foo {
+    extern "rust-call" fn call(&self, args: (isize, isize, isize)) -> Self::Output {
+        println!("{:?}", args);
+        (args.0 + 3, args.1 + 3, args.2 + 3)
+    }
+}
+
+impl FnMut<(isize, isize, isize)> for Foo {
+    extern "rust-call" fn call_mut(&mut self, args: (isize, isize, isize)) -> Self::Output {
+        println!("{:?}", args);
+        (args.0 + 3, args.1 + 3, args.2 + 3)
+    }
+}
+impl FnOnce<(isize, isize, isize)> for Foo {
+    type Output = (isize, isize, isize);
+    extern "rust-call" fn call_once(self, args: (isize, isize, isize)) -> Self::Output {
+        println!("{:?}", args);
+        (args.0 + 3, args.1 + 3, args.2 + 3)
+    }
+}
+
+fn main() {
+    let foo = Foo;
+    assert_eq!(foo(1, 1), (2, 2));
+    assert_eq!(foo(1, 1, 1), (4, 4, 4));
+}
diff --git a/src/test/run-pass/issue-45510.rs b/src/test/run-pass/issue-45510.rs
new file mode 100644
index 00000000000..9e104ce6c4f
--- /dev/null
+++ b/src/test/run-pass/issue-45510.rs
@@ -0,0 +1,32 @@
+// Test overloaded resolution of fn_traits.
+// run-pass
+
+#![feature(fn_traits)]
+#![feature(unboxed_closures)]
+
+#[derive(Debug, PartialEq, Eq)]
+struct Ishmael;
+#[derive(Debug, PartialEq, Eq)]
+struct Maybe;
+struct CallMe;
+
+impl FnOnce<(Ishmael,)> for CallMe {
+    type Output = Ishmael;
+    extern "rust-call" fn call_once(self, _args: (Ishmael,)) -> Ishmael {
+        println!("Split your lungs with blood and thunder!");
+        Ishmael
+    }
+}
+
+impl FnOnce<(Maybe,)> for CallMe {
+    type Output = Maybe;
+    extern "rust-call" fn call_once(self, _args: (Maybe,)) -> Maybe {
+        println!("So we just met, and this is crazy");
+        Maybe
+    }
+}
+
+fn main() {
+    assert_eq!(CallMe(Ishmael), Ishmael);
+    assert_eq!(CallMe(Maybe), Maybe);
+}