From 4e77c3a9b1ac25e3c01911e37e0b79cef93ec7ee Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 4 Nov 2018 11:27:09 +0100 Subject: [PATCH 1/7] test: Add test for issues 45510 and 18952. Those tests are directly taken from the comments on the bug reports. --- src/test/run-pass/issue-18952.rs | 48 ++++++++++++++++++++++++++++++++ src/test/run-pass/issue-45510.rs | 27 ++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/test/run-pass/issue-18952.rs create mode 100644 src/test/run-pass/issue-45510.rs diff --git a/src/test/run-pass/issue-18952.rs b/src/test/run-pass/issue-18952.rs new file mode 100644 index 0000000000000..ca2645ca9e5d9 --- /dev/null +++ b/src/test/run-pass/issue-18952.rs @@ -0,0 +1,48 @@ +// This issue tests fn_traits overloading on arity. + +#![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); + } +} + +impl FnMut<(isize, isize)> for Foo { + extern "rust-call" fn call_mut(&mut self, args: (isize, isize)) -> Self::Output { + println!("{:?}", args); + } +} + +impl FnOnce<(isize, isize)> for Foo { + type Output = (); + extern "rust-call" fn call_once(self, args: (isize, isize)) -> Self::Output { + println!("{:?}", args); + } +} + +impl Fn<(isize, isize, isize)> for Foo { + extern "rust-call" fn call(&self, args: (isize, isize, isize)) -> Self::Output { + println!("{:?}", args); + } +} + +impl FnMut<(isize, isize, isize)> for Foo { + extern "rust-call" fn call_mut(&mut self, args: (isize, isize, isize)) -> Self::Output { + println!("{:?}", args); + } +} +impl FnOnce<(isize, isize, isize)> for Foo { + type Output = (); + extern "rust-call" fn call_once(self, args: (isize, isize, isize)) -> Self::Output { + println!("{:?}", args); + } +} + +fn main() { + let foo = Foo; + foo(1, 1); +} diff --git a/src/test/run-pass/issue-45510.rs b/src/test/run-pass/issue-45510.rs new file mode 100644 index 0000000000000..922a26b903094 --- /dev/null +++ b/src/test/run-pass/issue-45510.rs @@ -0,0 +1,27 @@ +// Test overloaded resolution of fn_traits. + +#![feature(fn_traits)] +#![feature(unboxed_closures)] + +struct Ishmael; +struct Maybe; +struct CallMe; + +impl FnOnce<(Ishmael,)> for CallMe { + type Output = (); + extern "rust-call" fn call_once(self, _args: (Ishmael,)) -> () { + println!("Split your lungs with blood and thunder!"); + } +} + +impl FnOnce<(Maybe,)> for CallMe { + type Output = (); + extern "rust-call" fn call_once(self, _args: (Maybe,)) -> () { + println!("So we just met, and this is crazy"); + } +} + +fn main() { + CallMe(Ishmael); + CallMe(Maybe); +} From 6de9c13830c74551632f2a1b299778d9e62381ff Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 4 Nov 2018 16:14:44 +0100 Subject: [PATCH 2/7] rustfmt: librust_typeck/check/callee.rs --- src/librustc_typeck/check/callee.rs | 344 ++++++++++++++++------------ 1 file changed, 200 insertions(+), 144 deletions(-) diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index d4f639c070de3..727fbf7b30f67 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -1,16 +1,16 @@ -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_target::spec::abi; use syntax::ast::Ident; use syntax_pos::Span; -use errors::Applicability; use rustc::hir; @@ -33,12 +33,13 @@ 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); @@ -74,15 +75,17 @@ 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> { + fn try_overloaded_call_step( + &self, + call_expr: &'gcx hir::Expr, + callee_expr: &'gcx hir::Expr, + autoderef: &Autoderef<'a, 'gcx, 'tcx>, + ) -> Option> { 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 +103,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 +142,50 @@ 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) - }) + 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) + }) } - fn try_overloaded_call_traits(&self, - call_expr: &hir::Expr, - adjusted_ty: Ty<'tcx>) - -> Option<(Option>, - MethodCallee<'tcx>)> { + fn try_overloaded_call_traits( + &self, + call_expr: &hir::Expr, + adjusted_ty: Ty<'tcx>, + ) -> Option<(Option>, 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) { + if let Some(ok) = self.lookup_method_in_trait( + call_expr.span, + method_name, + trait_def_id, + adjusted_ty, + None, + ) { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; if borrow { @@ -173,11 +197,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 +212,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 +245,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 +265,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 +316,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 +343,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 +440,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) { Some((autoref, method_callee)) => { // One problem is that when we get here, we are going // to have a newly instantiated function signature @@ -410,22 +460,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" + ); } } } From 1827d52a3f39fd9b25cebf4d78f2e0926c0cb593 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 4 Nov 2018 11:23:45 +0100 Subject: [PATCH 3/7] rustc_typeck: Implement resolution advised in issue 45510. When resolving Fn traits, the compiler failed to take into account overloaded implementations. To resolve this, we inform the trait dispatch code that the arguments will becoming as a tuple of correct arity. --- src/librustc_typeck/check/callee.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 727fbf7b30f67..5a79bec4dd72d 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -8,6 +8,7 @@ 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::infer::type_variable::TypeVariableOrigin; use rustc_target::spec::abi; use syntax::ast::Ident; use syntax_pos::Span; @@ -46,7 +47,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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); @@ -79,6 +80,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { &self, call_expr: &'gcx hir::Expr, callee_expr: &'gcx hir::Expr, + arg_exprs: &'gcx [hir::Expr], autoderef: &Autoderef<'a, 'gcx, 'tcx>, ) -> Option> { let adjusted_ty = autoderef.unambiguous_final_ty(self); @@ -142,7 +144,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => {} } - self.try_overloaded_call_traits(call_expr, adjusted_ty) + self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) .map(|(autoref, method)| { let mut adjustments = autoderef.adjust_steps(self, Needs::None); adjustments.extend(autoref); @@ -155,6 +157,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { &self, call_expr: &hir::Expr, adjusted_ty: Ty<'tcx>, + opt_arg_exprs: Option<&'gcx [hir::Expr]>, ) -> Option<(Option>, MethodCallee<'tcx>)> { // Try the options that are least restrictive on the caller first. for &(opt_trait_def_id, method_name, borrow) in &[ @@ -179,12 +182,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { None => continue, }; + 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, - None, + opt_input_types, ) { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; @@ -445,7 +457,7 @@ impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> { .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 From f518cf9ba9f3580c9f6d1d9adf45c4490250c275 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 8 Nov 2018 10:39:46 +0100 Subject: [PATCH 4/7] Move tests to ui. --- src/test/{run-pass => ui}/issue-18952.rs | 1 + src/test/{run-pass => ui}/issue-45510.rs | 1 + 2 files changed, 2 insertions(+) rename src/test/{run-pass => ui}/issue-18952.rs (99%) rename src/test/{run-pass => ui}/issue-45510.rs (97%) diff --git a/src/test/run-pass/issue-18952.rs b/src/test/ui/issue-18952.rs similarity index 99% rename from src/test/run-pass/issue-18952.rs rename to src/test/ui/issue-18952.rs index ca2645ca9e5d9..08e48c232c91e 100644 --- a/src/test/run-pass/issue-18952.rs +++ b/src/test/ui/issue-18952.rs @@ -1,4 +1,5 @@ // This issue tests fn_traits overloading on arity. +// run-pass #![feature(fn_traits)] #![feature(unboxed_closures)] diff --git a/src/test/run-pass/issue-45510.rs b/src/test/ui/issue-45510.rs similarity index 97% rename from src/test/run-pass/issue-45510.rs rename to src/test/ui/issue-45510.rs index 922a26b903094..2fa1ee813c308 100644 --- a/src/test/run-pass/issue-45510.rs +++ b/src/test/ui/issue-45510.rs @@ -1,4 +1,5 @@ // Test overloaded resolution of fn_traits. +// run-pass #![feature(fn_traits)] #![feature(unboxed_closures)] From 2f42a188d9464427803a609905f12a40248c791d Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 21 Nov 2018 10:10:16 +0100 Subject: [PATCH 5/7] Fix failing diagnostic test. --- src/librustc_typeck/check/callee.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 5a79bec4dd72d..f10985a4d0eaa 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -145,6 +145,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } 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); From 2530ce9fa63a756d655b8e0801b4542c74ec4cae Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 21 Nov 2018 10:10:35 +0100 Subject: [PATCH 6/7] Add comment. --- src/librustc_typeck/check/callee.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index f10985a4d0eaa..c59c143f74b62 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -144,6 +144,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => {} } + // 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`, 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)| { From 91c155bcd7af54fba0b705de1a2c2d55fa9788fa Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 27 Nov 2018 15:15:27 +0100 Subject: [PATCH 7/7] Change return types and check return values in tests. This allows to verify that we handle different types as return types, and that we call the expected functions. --- src/test/{ui => run-pass}/issue-18952.rs | 13 ++++++++++--- src/test/{ui => run-pass}/issue-45510.rs | 16 ++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) rename src/test/{ui => run-pass}/issue-18952.rs (75%) rename src/test/{ui => run-pass}/issue-45510.rs (59%) diff --git a/src/test/ui/issue-18952.rs b/src/test/run-pass/issue-18952.rs similarity index 75% rename from src/test/ui/issue-18952.rs rename to src/test/run-pass/issue-18952.rs index 08e48c232c91e..56378b59e3642 100644 --- a/src/test/ui/issue-18952.rs +++ b/src/test/run-pass/issue-18952.rs @@ -9,41 +9,48 @@ 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 = (); + 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 = (); + 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; - foo(1, 1); + assert_eq!(foo(1, 1), (2, 2)); + assert_eq!(foo(1, 1, 1), (4, 4, 4)); } diff --git a/src/test/ui/issue-45510.rs b/src/test/run-pass/issue-45510.rs similarity index 59% rename from src/test/ui/issue-45510.rs rename to src/test/run-pass/issue-45510.rs index 2fa1ee813c308..9e104ce6c4f30 100644 --- a/src/test/ui/issue-45510.rs +++ b/src/test/run-pass/issue-45510.rs @@ -4,25 +4,29 @@ #![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 = (); - extern "rust-call" fn call_once(self, _args: (Ishmael,)) -> () { + 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 = (); - extern "rust-call" fn call_once(self, _args: (Maybe,)) -> () { + 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() { - CallMe(Ishmael); - CallMe(Maybe); + assert_eq!(CallMe(Ishmael), Ishmael); + assert_eq!(CallMe(Maybe), Maybe); }