From fceab9fb348becb62f404d0d9381a49446beac7a Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 14 Aug 2017 15:06:14 +0300 Subject: [PATCH 1/5] make probe priority logic clearer --- src/librustc_typeck/check/method/probe.rs | 66 ++++++++++------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 5e5a27f2ba137..30683eeeba89b 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -1116,21 +1116,17 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let steps = self.steps.clone(); // find the first step that works - steps.iter().filter_map(|step| self.pick_step(step)).next() - } - - fn pick_step(&mut self, step: &CandidateStep<'tcx>) -> Option> { - debug!("pick_step: step={:?}", step); - - if step.self_ty.references_error() { - return None; - } - - if let Some(result) = self.pick_by_value_method(step) { - return Some(result); - } - - self.pick_autorefd_method(step) + steps + .iter() + .filter(|step| { + debug!("pick_core: step={:?}", step); + !step.self_ty.references_error() + }).flat_map(|step| { + self.pick_by_value_method(step).or_else(|| { + self.pick_autorefd_method(step, hir::MutImmutable).or_else(|| { + self.pick_autorefd_method(step, hir::MutMutable) + })})}) + .next() } fn pick_by_value_method(&mut self, step: &CandidateStep<'tcx>) -> Option> { @@ -1161,36 +1157,30 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { }) } - fn pick_autorefd_method(&mut self, step: &CandidateStep<'tcx>) -> Option> { + fn pick_autorefd_method(&mut self, step: &CandidateStep<'tcx>, mutbl: hir::Mutability) + -> Option> { let tcx = self.tcx; // In general, during probing we erase regions. See // `impl_self_ty()` for an explanation. let region = tcx.types.re_erased; - // Search through mutabilities in order to find one where pick works: - [hir::MutImmutable, hir::MutMutable] - .iter() - .filter_map(|&m| { - let autoref_ty = tcx.mk_ref(region, - ty::TypeAndMut { - ty: step.self_ty, - mutbl: m, - }); - self.pick_method(autoref_ty).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; - pick.autoref = Some(m); - pick.unsize = if step.unsize { - Some(step.self_ty) - } else { - None - }; - pick - }) - }) + let autoref_ty = tcx.mk_ref(region, + ty::TypeAndMut { + ty: step.self_ty, mutbl + }); + self.pick_method(autoref_ty).map(|r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref = Some(mutbl); + pick.unsize = if step.unsize { + Some(step.self_ty) + } else { + None + }; + pick }) - .nth(0) + }) } fn pick_method(&mut self, self_ty: Ty<'tcx>) -> Option> { From 9be155d88e44c1e4cdacfc640448fc9305151ab2 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 15 Aug 2017 13:41:54 +0300 Subject: [PATCH 2/5] remove the hacky selection impl in `method::probe` --- src/librustc_typeck/check/method/confirm.rs | 18 - src/librustc_typeck/check/method/mod.rs | 5 - src/librustc_typeck/check/method/probe.rs | 505 ++++-------------- src/librustc_typeck/check/method/suggest.rs | 16 - ...xed-closures-infer-explicit-call-early.rs} | 8 +- .../no-method-suggested-traits.stderr | 4 + .../ui/mismatched_types/issue-36053-2.stderr | 2 +- 7 files changed, 120 insertions(+), 438 deletions(-) rename src/test/{compile-fail/unboxed-closures-infer-explicit-call-too-early.rs => run-pass/unboxed-closures-infer-explicit-call-early.rs} (79%) diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index db383b6305b4a..08ec3bf74a71d 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -232,24 +232,6 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { }) } - probe::ExtensionImplPick(impl_def_id) => { - // The method being invoked is the method as defined on the trait, - // so return the substitutions from the trait. Consider: - // - // impl Trait for Foo { ... } - // - // If we instantiate A, B, and C with $A, $B, and $C - // respectively, then we want to return the type - // parameters from the trait ([$A,$B]), not those from - // the impl ([$A,$B,$C]) not the receiver type ([$C]). - let impl_polytype = self.impl_self_ty(self.span, impl_def_id); - let impl_trait_ref = - self.instantiate_type_scheme(self.span, - impl_polytype.substs, - &self.tcx.impl_trait_ref(impl_def_id).unwrap()); - impl_trait_ref.substs - } - probe::TraitPick => { let trait_def_id = pick.item.container.id(); diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 819f48a1b57e8..caf1ab7cf831d 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -52,10 +52,6 @@ pub enum MethodError<'tcx> { // Multiple methods might apply. Ambiguity(Vec), - // Using a `Fn`/`FnMut`/etc method on a raw closure type before we have inferred its kind. - ClosureAmbiguity(// DefId of fn trait - DefId), - // Found an applicable method, but it is not visible. The second argument contains a list of // not-in-scope traits which may work. PrivateMatch(Def, Vec), @@ -113,7 +109,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Ok(..) => true, Err(NoMatch(..)) => false, Err(Ambiguity(..)) => true, - Err(ClosureAmbiguity(..)) => true, Err(PrivateMatch(..)) => allow_private, Err(IllegalSizedBound(..)) => true, } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 30683eeeba89b..f63db891bfb06 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -18,7 +18,7 @@ use hir::def_id::DefId; use hir::def::Def; use rustc::ty::subst::{Subst, Substs}; use rustc::traits::{self, ObligationCause}; -use rustc::ty::{self, Ty, ToPolyTraitRef, TraitRef, TypeFoldable}; +use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable}; use rustc::infer::type_variable::TypeVariableOrigin; use rustc::util::nodemap::FxHashSet; use rustc::infer::{self, InferOk}; @@ -51,7 +51,6 @@ struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { mode: Mode, looking_for: LookingFor<'tcx>, steps: Rc>>, - opt_simplified_steps: Option>, inherent_candidates: Vec>, extension_candidates: Vec>, impl_dups: FxHashSet, @@ -95,13 +94,8 @@ enum CandidateKind<'tcx> { InherentImplCandidate(&'tcx Substs<'tcx>, // Normalize obligations Vec>), - ExtensionImplCandidate(// Impl - DefId, - &'tcx Substs<'tcx>, - // Normalize obligations - Vec>), ObjectCandidate, - TraitCandidate, + TraitCandidate(ty::TraitRef<'tcx>), WhereClauseCandidate(// Trait ty::PolyTraitRef<'tcx>), } @@ -133,8 +127,6 @@ pub struct Pick<'tcx> { #[derive(Clone, Debug, PartialEq, Eq)] pub enum PickKind<'tcx> { InherentImplPick, - ExtensionImplPick(// Impl - DefId), ObjectPick, TraitPick, WhereClausePick(// Trait @@ -259,24 +251,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }] }; - // Create a list of simplified self types, if we can. - let mut simplified_steps = Vec::new(); - for step in &steps { - match ty::fast_reject::simplify_type(self.tcx, step.self_ty, true) { - None => { - break; - } - Some(simplified_type) => { - simplified_steps.push(simplified_type); - } - } - } - let opt_simplified_steps = if simplified_steps.len() < steps.len() { - None // failed to convert at least one of the steps - } else { - Some(simplified_steps) - }; - debug!("ProbeContext: steps for self_ty={:?} are {:?}", self_ty, steps); @@ -285,8 +259,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // that we create during the probe process are removed later self.probe(|_| { let mut probe_cx = - ProbeContext::new(self, span, mode, looking_for, - steps, opt_simplified_steps); + ProbeContext::new(self, span, mode, looking_for, steps); probe_cx.assemble_inherent_candidates(); match scope { @@ -357,8 +330,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { span: Span, mode: Mode, looking_for: LookingFor<'tcx>, - steps: Vec>, - opt_simplified_steps: Option>) + steps: Vec>) -> ProbeContext<'a, 'gcx, 'tcx> { ProbeContext { fcx, @@ -369,7 +341,6 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { extension_candidates: Vec::new(), impl_dups: FxHashSet(), steps: Rc::new(steps), - opt_simplified_steps, static_candidates: Vec::new(), private_candidate: None, unsatisfied_predicates: Vec::new(), @@ -710,6 +681,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { -> Result<(), MethodError<'tcx>> { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); + let trait_substs = self.fresh_item_substs(trait_def_id); + let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs); for item in self.impl_or_trait_item(trait_def_id) { // Check whether `trait_def_id` defines a method with suitable name: @@ -719,317 +692,14 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { continue; } - self.assemble_builtin_candidates(import_id, trait_def_id, item.clone()); - - self.assemble_extension_candidates_for_trait_impls(import_id, trait_def_id, - item.clone()); - - self.assemble_closure_candidates(import_id, trait_def_id, item.clone())?; - - self.assemble_generator_candidates(import_id, trait_def_id, item.clone())?; - - self.assemble_projection_candidates(import_id, trait_def_id, item.clone()); - - self.assemble_where_clause_candidates(import_id, trait_def_id, item.clone()); - } - - Ok(()) - } - - fn assemble_builtin_candidates(&mut self, - import_id: Option, - trait_def_id: DefId, - item: ty::AssociatedItem) { - if Some(trait_def_id) == self.tcx.lang_items.clone_trait() { - self.assemble_builtin_clone_candidates(import_id, trait_def_id, item); - } - } - - fn assemble_builtin_clone_candidates(&mut self, - import_id: Option, - trait_def_id: DefId, - item: ty::AssociatedItem) { - for step in Rc::clone(&self.steps).iter() { - match step.self_ty.sty { - ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) | - ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) | - ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyChar | - ty::TyRawPtr(..) | ty::TyError | ty::TyNever | - ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) | - ty::TyArray(..) | ty::TyTuple(..) => { - () - } - - _ => continue, - }; - - let substs = Substs::for_item(self.tcx, - trait_def_id, - |def, _| self.region_var_for_def(self.span, def), - |def, substs| { - if def.index == 0 { - step.self_ty - } else { - self.type_var_for_def(self.span, def, substs) - } - }); - - let xform_self_ty = self.xform_self_ty(&item, step.self_ty, substs); - self.push_inherent_candidate(xform_self_ty, item, TraitCandidate, import_id); - } - } - - fn assemble_extension_candidates_for_trait_impls(&mut self, - import_id: Option, - trait_def_id: DefId, - item: ty::AssociatedItem) { - // FIXME(arielb1): can we use for_each_relevant_impl here? - self.tcx.for_each_impl(trait_def_id, |impl_def_id| { - debug!("assemble_extension_candidates_for_trait_impl: trait_def_id={:?} \ - impl_def_id={:?}", - trait_def_id, - impl_def_id); - - if !self.impl_can_possibly_match(impl_def_id) { - return; - } - - let (_, impl_substs) = self.impl_ty_and_substs(impl_def_id); - - debug!("impl_substs={:?}", impl_substs); - - let impl_trait_ref = self.tcx.impl_trait_ref(impl_def_id) - .unwrap() // we know this is a trait impl - .subst(self.tcx, impl_substs); - - debug!("impl_trait_ref={:?}", impl_trait_ref); - - // Determine the receiver type that the method itself expects. - let xform_self_ty = - self.xform_self_ty(&item, impl_trait_ref.self_ty(), impl_trait_ref.substs); - - // Normalize the receiver. We can't use normalize_associated_types_in - // as it will pollute the fcx's fulfillment context after this probe - // is over. - let cause = traits::ObligationCause::misc(self.span, self.body_id); - let selcx = &mut traits::SelectionContext::new(self.fcx); - let traits::Normalized { value: xform_self_ty, obligations } = - traits::normalize(selcx, self.param_env, cause, &xform_self_ty); - - debug!("xform_self_ty={:?}", xform_self_ty); - + let xform_self_ty = self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs); self.push_extension_candidate(xform_self_ty, item, - ExtensionImplCandidate(impl_def_id, impl_substs, obligations), import_id); - }); - } - - fn impl_can_possibly_match(&self, impl_def_id: DefId) -> bool { - let simplified_steps = match self.opt_simplified_steps { - Some(ref simplified_steps) => simplified_steps, - None => { - return true; - } - }; - - let impl_type = self.tcx.type_of(impl_def_id); - let impl_simplified_type = - match ty::fast_reject::simplify_type(self.tcx, impl_type, false) { - Some(simplified_type) => simplified_type, - None => { - return true; - } - }; - - simplified_steps.contains(&impl_simplified_type) - } - - fn assemble_closure_candidates(&mut self, - import_id: Option, - trait_def_id: DefId, - item: ty::AssociatedItem) - -> Result<(), MethodError<'tcx>> { - // Check if this is one of the Fn,FnMut,FnOnce traits. - let tcx = self.tcx; - let kind = if Some(trait_def_id) == tcx.lang_items.fn_trait() { - ty::ClosureKind::Fn - } else if Some(trait_def_id) == tcx.lang_items.fn_mut_trait() { - ty::ClosureKind::FnMut - } else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() { - ty::ClosureKind::FnOnce - } else { - return Ok(()); - }; - - // Check if there is an unboxed-closure self-type in the list of receivers. - // If so, add "synthetic impls". - let steps = self.steps.clone(); - for step in steps.iter() { - let closure_id = match step.self_ty.sty { - ty::TyClosure(def_id, _) => { - if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { - self.tcx.hir.node_to_hir_id(id) - } else { - continue; - } - } - _ => continue, - }; - - let closure_kind = { - match self.tables.borrow().closure_kinds().get(closure_id) { - Some(&(k, _)) => k, - None => { - return Err(MethodError::ClosureAmbiguity(trait_def_id)); - } - } - }; - - // this closure doesn't implement the right kind of `Fn` trait - if !closure_kind.extends(kind) { - continue; - } - - // create some substitutions for the argument/return type; - // for the purposes of our method lookup, we only take - // receiver type into account, so we can just substitute - // fresh types here to use during substitution and subtyping. - let substs = Substs::for_item(self.tcx, - trait_def_id, - |def, _| self.region_var_for_def(self.span, def), - |def, substs| { - if def.index == 0 { - step.self_ty - } else { - self.type_var_for_def(self.span, def, substs) - } - }); - - let xform_self_ty = self.xform_self_ty(&item, step.self_ty, substs); - self.push_inherent_candidate(xform_self_ty, item, TraitCandidate, import_id); + TraitCandidate(trait_ref), + import_id); } - Ok(()) } - fn assemble_generator_candidates(&mut self, - import_id: Option, - trait_def_id: DefId, - item: ty::AssociatedItem) - -> Result<(), MethodError<'tcx>> { - // Check if this is the Generator trait. - let tcx = self.tcx; - if Some(trait_def_id) != tcx.lang_items.gen_trait() { - return Ok(()); - } - - // Check if there is an generator self-type in the list of receivers. - // If so, add "synthetic impls". - let steps = self.steps.clone(); - for step in steps.iter() { - match step.self_ty.sty { - ty::TyGenerator(..) => (), - _ => continue, - }; - - // create some substitutions for the argument/return type; - // for the purposes of our method lookup, we only take - // receiver type into account, so we can just substitute - // fresh types here to use during substitution and subtyping. - let substs = Substs::for_item(self.tcx, - trait_def_id, - |def, _| self.region_var_for_def(self.span, def), - |def, substs| { - if def.index == 0 { - step.self_ty - } else { - self.type_var_for_def(self.span, def, substs) - } - }); - - let xform_self_ty = self.xform_self_ty(&item, step.self_ty, substs); - self.push_inherent_candidate(xform_self_ty, item, TraitCandidate, import_id); - } - - Ok(()) - } - - fn assemble_projection_candidates(&mut self, - import_id: Option, - trait_def_id: DefId, - item: ty::AssociatedItem) { - debug!("assemble_projection_candidates(\ - trait_def_id={:?}, \ - item={:?})", - trait_def_id, - item); - - for step in Rc::clone(&self.steps).iter() { - debug!("assemble_projection_candidates: step={:?}", step); - - let (def_id, substs) = match step.self_ty.sty { - ty::TyProjection(ref data) => { - let trait_ref = data.trait_ref(self.tcx); - (trait_ref.def_id, trait_ref.substs) - }, - ty::TyAnon(def_id, substs) => (def_id, substs), - _ => continue, - }; - - debug!("assemble_projection_candidates: def_id={:?} substs={:?}", - def_id, - substs); - - let trait_predicates = self.tcx.predicates_of(def_id); - let bounds = trait_predicates.instantiate(self.tcx, substs); - let predicates = bounds.predicates; - debug!("assemble_projection_candidates: predicates={:?}", - predicates); - for poly_bound in traits::elaborate_predicates(self.tcx, predicates) - .filter_map(|p| p.to_opt_poly_trait_ref()) - .filter(|b| b.def_id() == trait_def_id) { - let bound = self.erase_late_bound_regions(&poly_bound); - - debug!("assemble_projection_candidates: def_id={:?} substs={:?} bound={:?}", - def_id, - substs, - bound); - - if self.can_eq(self.param_env, step.self_ty, bound.self_ty()).is_ok() { - let xform_self_ty = self.xform_self_ty(&item, bound.self_ty(), bound.substs); - - debug!("assemble_projection_candidates: bound={:?} xform_self_ty={:?}", - bound, - xform_self_ty); - - self.push_extension_candidate(xform_self_ty, item, TraitCandidate, import_id); - } - } - } - } - - fn assemble_where_clause_candidates(&mut self, - import_id: Option, - trait_def_id: DefId, - item: ty::AssociatedItem) { - debug!("assemble_where_clause_candidates(trait_def_id={:?})", - trait_def_id); - - let caller_predicates = self.param_env.caller_bounds.to_vec(); - for poly_bound in traits::elaborate_predicates(self.tcx, caller_predicates) - .filter_map(|p| p.to_opt_poly_trait_ref()) - .filter(|b| b.def_id() == trait_def_id) { - let bound = self.erase_late_bound_regions(&poly_bound); - let xform_self_ty = self.xform_self_ty(&item, bound.self_ty(), bound.substs); - - debug!("assemble_where_clause_candidates: bound={:?} xform_self_ty={:?}", - bound, - xform_self_ty); - - self.push_extension_candidate(xform_self_ty, item, - WhereClauseCandidate(poly_bound), import_id); - } - } - fn candidate_method_names(&self) -> Vec { let mut set = FxHashSet(); let mut names: Vec<_> = @@ -1095,10 +765,6 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { assert!(others.is_empty()); vec![] } - Some(Err(MethodError::ClosureAmbiguity(..))) => { - // this error only occurs when assembling candidates - span_bug!(span, "encountered ClosureAmbiguity from pick_core"); - } _ => vec![], }; @@ -1226,13 +892,49 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { } if applicable_candidates.len() > 1 { - let sources = probes.iter().map(|p| p.to_source()).collect(); + let sources = probes.iter() + .map(|p| self.candidate_source(p, self_ty)) + .collect(); return Some(Err(MethodError::Ambiguity(sources))); } applicable_candidates.pop().map(|probe| Ok(probe.to_unadjusted_pick())) } + fn select_trait_candidate(&self, trait_ref: ty::TraitRef<'tcx>) + -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> + { + let cause = traits::ObligationCause::misc(self.span, self.body_id); + let predicate = + trait_ref.to_poly_trait_ref().to_poly_trait_predicate(); + let obligation = traits::Obligation::new(cause, self.param_env, predicate); + traits::SelectionContext::new(self).select(&obligation) + } + + fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) + -> CandidateSource + { + match candidate.kind { + InherentImplCandidate(..) => ImplSource(candidate.item.container.id()), + ObjectCandidate | + WhereClauseCandidate(_) => TraitSource(candidate.item.container.id()), + TraitCandidate(trait_ref) => self.probe(|_| { + let _ = self.at(&ObligationCause::dummy(), self.param_env) + .sup(candidate.xform_self_ty, self_ty); + match self.select_trait_candidate(trait_ref) { + Ok(Some(traits::Vtable::VtableImpl(ref impl_data))) => { + // If only a single impl matches, make the error message point + // to that impl. + ImplSource(impl_data.impl_def_id) + } + _ => { + TraitSource(candidate.item.container.id()) + } + } + }) + } + } + fn consider_probe(&self, self_ty: Ty<'tcx>, probe: &Candidate<'tcx>, @@ -1251,56 +953,80 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { } }; + let mut successful = true; + let selcx = &mut traits::SelectionContext::new(self); + let cause = traits::ObligationCause::misc(self.span, self.body_id); + // If so, impls may carry other conditions (e.g., where // clauses) that must be considered. Make sure that those // match as well (or at least may match, sometimes we // don't have enough information to fully evaluate). - let (impl_def_id, substs, ref_obligations) = match probe.kind { + let candidate_obligations : Vec<_> = match probe.kind { InherentImplCandidate(ref substs, ref ref_obligations) => { - (probe.item.container.id(), substs, ref_obligations) - } - - ExtensionImplCandidate(impl_def_id, ref substs, ref ref_obligations) => { - (impl_def_id, substs, ref_obligations) + // Check whether the impl imposes obligations we have to worry about. + let impl_def_id = probe.item.container.id(); + let impl_bounds = self.tcx.predicates_of(impl_def_id); + let impl_bounds = impl_bounds.instantiate(self.tcx, substs); + let traits::Normalized { value: impl_bounds, obligations: norm_obligations } = + traits::normalize(selcx, self.param_env, cause.clone(), &impl_bounds); + + // Convert the bounds into obligations. + let impl_obligations = traits::predicates_for_generics( + cause.clone(), self.param_env, &impl_bounds); + + debug!("impl_obligations={:?}", impl_obligations); + impl_obligations.into_iter() + .chain(norm_obligations.into_iter()) + .chain(ref_obligations.iter().cloned()) + .collect() } ObjectCandidate | - TraitCandidate | WhereClauseCandidate(..) => { // These have no additional conditions to check. - return true; + vec![] } - }; - let selcx = &mut traits::SelectionContext::new(self); - let cause = traits::ObligationCause::misc(self.span, self.body_id); - - // Check whether the impl imposes obligations we have to worry about. - let impl_bounds = self.tcx.predicates_of(impl_def_id); - let impl_bounds = impl_bounds.instantiate(self.tcx, substs); - let traits::Normalized { value: impl_bounds, obligations: norm_obligations } = - traits::normalize(selcx, self.param_env, cause.clone(), &impl_bounds); + TraitCandidate(trait_ref) => { + let predicate = trait_ref.to_predicate(); + let obligation = + traits::Obligation::new(cause.clone(), self.param_env, predicate); + if !selcx.evaluate_obligation(&obligation) { + if self.probe(|_| self.select_trait_candidate(trait_ref).is_err()) { + // This candidate's primary obligation doesn't even + // select - don't bother registering anything in + // `potentially_unsatisfied_predicates`. + return false + } else { + // Some nested subobligation of this predicate + // failed. + // + // FIXME: try to find the exact nested subobligation + // and point at it rather than reporting the entire + // trait-ref? + successful = false; + let trait_ref = self.resolve_type_vars_if_possible(&trait_ref); + possibly_unsatisfied_predicates.push(trait_ref); + } + } + vec![] + } + }; - // Convert the bounds into obligations. - let obligations = traits::predicates_for_generics(cause.clone(), - self.param_env, - &impl_bounds); - debug!("impl_obligations={:?}", obligations); + debug!("consider_probe - candidate_obligations={:?} sub_obligations={:?}", + candidate_obligations, sub_obligations); // Evaluate those obligations to see if they might possibly hold. - let mut all_true = true; - for o in obligations.iter() - .chain(sub_obligations.iter()) - .chain(norm_obligations.iter()) - .chain(ref_obligations.iter()) { - if !selcx.evaluate_obligation(o) { - all_true = false; + for o in candidate_obligations.into_iter().chain(sub_obligations) { + let o = self.resolve_type_vars_if_possible(&o); + if !selcx.evaluate_obligation(&o) { + successful = false; if let &ty::Predicate::Trait(ref pred) = &o.predicate { possibly_unsatisfied_predicates.push(pred.0.trait_ref); } } } - all_true + successful }) } @@ -1434,16 +1160,16 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { /// Get the type of an impl and generate substitutions with placeholders. fn impl_ty_and_substs(&self, impl_def_id: DefId) -> (Ty<'tcx>, &'tcx Substs<'tcx>) { - let impl_ty = self.tcx.type_of(impl_def_id); - - let substs = Substs::for_item(self.tcx, - impl_def_id, - |_, _| self.tcx.types.re_erased, - |_, _| self.next_ty_var( - TypeVariableOrigin::SubstitutionPlaceholder( - self.tcx.def_span(impl_def_id)))); + (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) + } - (impl_ty, substs) + fn fresh_item_substs(&self, def_id: DefId) -> &'tcx Substs<'tcx> { + Substs::for_item(self.tcx, + def_id, + |_, _| self.tcx.types.re_erased, + |_, _| self.next_ty_var( + TypeVariableOrigin::SubstitutionPlaceholder( + self.tcx.def_span(def_id)))) } /// Replace late-bound-regions bound by `value` with `'static` using @@ -1493,9 +1219,8 @@ impl<'tcx> Candidate<'tcx> { item: self.item.clone(), kind: match self.kind { InherentImplCandidate(..) => InherentImplPick, - ExtensionImplCandidate(def_id, ..) => ExtensionImplPick(def_id), ObjectCandidate => ObjectPick, - TraitCandidate => TraitPick, + TraitCandidate(_) => TraitPick, WhereClauseCandidate(ref trait_ref) => { // Only trait derived from where-clauses should // appear here, so they should not contain any @@ -1513,14 +1238,4 @@ impl<'tcx> Candidate<'tcx> { unsize: None, } } - - fn to_source(&self) -> CandidateSource { - match self.kind { - InherentImplCandidate(..) => ImplSource(self.item.container.id()), - ExtensionImplCandidate(def_id, ..) => ImplSource(def_id), - ObjectCandidate | - TraitCandidate | - WhereClauseCandidate(_) => TraitSource(self.item.container.id()), - } - } } diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index c8b828f3a434d..9079ba4a89382 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -296,22 +296,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { err.emit(); } - MethodError::ClosureAmbiguity(trait_def_id) => { - let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \ - invoked on this closure as we have not yet inferred what \ - kind of closure it is", - item_name, - self.tcx.item_path_str(trait_def_id)); - let msg = if let Some(callee) = rcvr_expr { - format!("{}; use overloaded call notation instead (e.g., `{}()`)", - msg, - self.tcx.hir.node_to_pretty_string(callee.id)) - } else { - msg - }; - self.sess().span_err(span, &msg); - } - MethodError::PrivateMatch(def, out_of_scope_traits) => { let mut err = struct_span_err!(self.tcx.sess, span, E0624, "{} `{}` is private", def.kind_name(), item_name); diff --git a/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs b/src/test/run-pass/unboxed-closures-infer-explicit-call-early.rs similarity index 79% rename from src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs rename to src/test/run-pass/unboxed-closures-infer-explicit-call-early.rs index 62f6ee56ca5de..028f2e9375b3c 100644 --- a/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs +++ b/src/test/run-pass/unboxed-closures-infer-explicit-call-early.rs @@ -8,8 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(fn_traits)] + fn main() { - let mut zero = || {}; - let () = zero.call_mut(()); - //~^ ERROR we have not yet inferred what kind of closure it is + let mut zero = || 0; + let x = zero.call_mut(()); + assert_eq!(x, 0); } diff --git a/src/test/ui/impl-trait/no-method-suggested-traits.stderr b/src/test/ui/impl-trait/no-method-suggested-traits.stderr index fc441f9484273..23f115858cd5e 100644 --- a/src/test/ui/impl-trait/no-method-suggested-traits.stderr +++ b/src/test/ui/impl-trait/no-method-suggested-traits.stderr @@ -8,6 +8,8 @@ error[E0599]: no method named `method` found for type `u32` in the current scope = note: the following traits are implemented but not in scope, perhaps add a `use` for one of them: candidate #1: `use foo::Bar;` candidate #2: `use no_method_suggested_traits::foo::PubPub;` + candidate #3: `use no_method_suggested_traits::qux::PrivPub;` + candidate #4: `use no_method_suggested_traits::Reexported;` error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&u32>>` in the current scope --> $DIR/no-method-suggested-traits.rs:38:44 @@ -19,6 +21,8 @@ error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::box = note: the following traits are implemented but not in scope, perhaps add a `use` for one of them: candidate #1: `use foo::Bar;` candidate #2: `use no_method_suggested_traits::foo::PubPub;` + candidate #3: `use no_method_suggested_traits::qux::PrivPub;` + candidate #4: `use no_method_suggested_traits::Reexported;` error[E0599]: no method named `method` found for type `char` in the current scope --> $DIR/no-method-suggested-traits.rs:44:9 diff --git a/src/test/ui/mismatched_types/issue-36053-2.stderr b/src/test/ui/mismatched_types/issue-36053-2.stderr index 174f7dfa0d0f5..e2e2019307f34 100644 --- a/src/test/ui/mismatched_types/issue-36053-2.stderr +++ b/src/test/ui/mismatched_types/issue-36053-2.stderr @@ -5,8 +5,8 @@ error[E0599]: no method named `count` found for type `std::iter::Filter` `std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:17:39: 17:53]> : std::iter::Iterator` + `&mut std::iter::Filter>, [closure@$DIR/issue-36053-2.rs:17:39: 17:53]> : std::iter::Iterator` error[E0281]: type mismatch: `[closure@$DIR/issue-36053-2.rs:17:39: 17:53]` implements the trait `for<'r> std::ops::FnMut<(&'r str,)>`, but the trait `for<'r> std::ops::FnMut<(&'r &str,)>` is required --> $DIR/issue-36053-2.rs:17:32 From 6c11b47745265dd48fe680bc2e8865e52a571408 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 15 Aug 2017 23:30:13 +0300 Subject: [PATCH 3/5] remove the data from ClosureCandidate the data serves no purpose - it can be recovered from the obligation - and I think may leak stale inference variables into global caches. --- src/librustc/traits/select.rs | 186 +++++++++++++++------------------- 1 file changed, 80 insertions(+), 106 deletions(-) diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 44b8af3c1df98..68b080ccb95da 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -194,13 +194,12 @@ enum SelectionCandidate<'tcx> { ProjectionCandidate, /// Implementation of a `Fn`-family trait by one of the anonymous types - /// generated for a `||` expression. The ty::ClosureKind informs the - /// confirmation step what ClosureKind obligation to emit. - ClosureCandidate(/* closure */ DefId, ty::ClosureSubsts<'tcx>, ty::ClosureKind), + /// generated for a `||` expression. + ClosureCandidate, /// Implementation of a `Generator` trait by one of the anonymous types /// generated for a generator. - GeneratorCandidate(/* function / closure */ DefId, ty::ClosureSubsts<'tcx>), + GeneratorCandidate, /// Implementation of a `Fn`-family trait by one of the anonymous /// types generated for a fn pointer type (e.g., `fn(int)->int`) @@ -229,20 +228,12 @@ impl<'a, 'tcx> ty::Lift<'tcx> for SelectionCandidate<'a> { ObjectCandidate => ObjectCandidate, BuiltinObjectCandidate => BuiltinObjectCandidate, BuiltinUnsizeCandidate => BuiltinUnsizeCandidate, + ClosureCandidate => ClosureCandidate, + GeneratorCandidate => GeneratorCandidate, ParamCandidate(ref trait_ref) => { return tcx.lift(trait_ref).map(ParamCandidate); } - GeneratorCandidate(def_id, ref substs) => { - return tcx.lift(substs).map(|substs| { - GeneratorCandidate(def_id, substs) - }); - } - ClosureCandidate(def_id, ref substs, kind) => { - return tcx.lift(substs).map(|substs| { - ClosureCandidate(def_id, substs, kind) - }); - } }) } } @@ -1518,23 +1509,22 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // touch bound regions, they just capture the in-scope // type/region parameters let self_ty = *obligation.self_ty().skip_binder(); - let (closure_def_id, substs) = match self_ty.sty { - ty::TyGenerator(id, substs, _) => (id, substs), + match self_ty.sty { + ty::TyGenerator(..) => { + debug!("assemble_generator_candidates: self_ty={:?} obligation={:?}", + self_ty, + obligation); + + candidates.vec.push(GeneratorCandidate); + Ok(()) + } ty::TyInfer(ty::TyVar(_)) => { debug!("assemble_generator_candidates: ambiguous self-type"); candidates.ambiguous = true; return Ok(()); } _ => { return Ok(()); } - }; - - debug!("assemble_generator_candidates: self_ty={:?} obligation={:?}", - self_ty, - obligation); - - candidates.vec.push(GeneratorCandidate(closure_def_id, substs)); - - Ok(()) + } } /// Check for the artificial impl that the compiler will create for an obligation like `X : @@ -1556,36 +1546,31 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // ok to skip binder because the substs on closure types never // touch bound regions, they just capture the in-scope // type/region parameters - let self_ty = *obligation.self_ty().skip_binder(); - let (closure_def_id, substs) = match self_ty.sty { - ty::TyClosure(id, substs) => (id, substs), + match obligation.self_ty().skip_binder().sty { + ty::TyClosure(closure_def_id, _) => { + debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", + kind, obligation); + match self.infcx.closure_kind(closure_def_id) { + Some(closure_kind) => { + debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); + if closure_kind.extends(kind) { + candidates.vec.push(ClosureCandidate); + } + } + None => { + debug!("assemble_unboxed_candidates: closure_kind not yet known"); + candidates.vec.push(ClosureCandidate); + } + }; + Ok(()) + } ty::TyInfer(ty::TyVar(_)) => { debug!("assemble_unboxed_closure_candidates: ambiguous self-type"); candidates.ambiguous = true; return Ok(()); } _ => { return Ok(()); } - }; - - debug!("assemble_unboxed_candidates: self_ty={:?} kind={:?} obligation={:?}", - self_ty, - kind, - obligation); - - match self.infcx.closure_kind(closure_def_id) { - Some(closure_kind) => { - debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); - if closure_kind.extends(kind) { - candidates.vec.push(ClosureCandidate(closure_def_id, substs, kind)); - } - } - None => { - debug!("assemble_unboxed_candidates: closure_kind not yet known"); - candidates.vec.push(ClosureCandidate(closure_def_id, substs, kind)); - } } - - Ok(()) } /// Implement one of the `Fn()` family for a fn pointer. @@ -1902,8 +1887,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { when there are other valid candidates"); } ImplCandidate(..) | - ClosureCandidate(..) | - GeneratorCandidate(..) | + ClosureCandidate | + GeneratorCandidate | FnPointerCandidate | BuiltinObjectCandidate | BuiltinUnsizeCandidate | @@ -2245,15 +2230,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Ok(VtableImpl(self.confirm_impl_candidate(obligation, impl_def_id))) } - ClosureCandidate(closure_def_id, substs, kind) => { - let vtable_closure = - self.confirm_closure_candidate(obligation, closure_def_id, substs, kind)?; + ClosureCandidate => { + let vtable_closure = self.confirm_closure_candidate(obligation)?; Ok(VtableClosure(vtable_closure)) } - GeneratorCandidate(closure_def_id, substs) => { - let vtable_generator = - self.confirm_generator_candidate(obligation, closure_def_id, substs)?; + GeneratorCandidate => { + let vtable_generator = self.confirm_generator_candidate(obligation)?; Ok(VtableGenerator(vtable_generator)) } @@ -2590,21 +2573,34 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } fn confirm_generator_candidate(&mut self, - obligation: &TraitObligation<'tcx>, - closure_def_id: DefId, - substs: ty::ClosureSubsts<'tcx>) - -> Result>, + obligation: &TraitObligation<'tcx>) + -> Result>, SelectionError<'tcx>> { + // ok to skip binder because the substs on generator types never + // touch bound regions, they just capture the in-scope + // type/region parameters + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let (closure_def_id, substs) = match self_ty.sty { + ty::TyGenerator(id, substs, _) => (id, substs), + _ => bug!("closure candidate for non-closure {:?}", obligation) + }; + debug!("confirm_generator_candidate({:?},{:?},{:?})", obligation, closure_def_id, substs); + let trait_ref = + self.generator_trait_ref_unnormalized(obligation, closure_def_id, substs); let Normalized { value: trait_ref, obligations - } = self.generator_trait_ref(obligation, closure_def_id, substs); + } = normalize_with_depth(self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth+1, + &trait_ref); debug!("confirm_generator_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})", closure_def_id, @@ -2624,22 +2620,36 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } fn confirm_closure_candidate(&mut self, - obligation: &TraitObligation<'tcx>, - closure_def_id: DefId, - substs: ty::ClosureSubsts<'tcx>, - kind: ty::ClosureKind) + obligation: &TraitObligation<'tcx>) -> Result>, SelectionError<'tcx>> { - debug!("confirm_closure_candidate({:?},{:?},{:?})", - obligation, - closure_def_id, - substs); + debug!("confirm_closure_candidate({:?})", obligation); + + let kind = match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) { + Some(k) => k, + None => bug!("closure candidate for non-fn trait {:?}", obligation) + }; + + // ok to skip binder because the substs on closure types never + // touch bound regions, they just capture the in-scope + // type/region parameters + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let (closure_def_id, substs) = match self_ty.sty { + ty::TyClosure(id, substs) => (id, substs), + _ => bug!("closure candidate for non-closure {:?}", obligation) + }; + let trait_ref = + self.closure_trait_ref_unnormalized(obligation, closure_def_id, substs); let Normalized { value: trait_ref, mut obligations - } = self.closure_trait_ref(obligation, closure_def_id, substs); + } = normalize_with_depth(self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth+1, + &trait_ref); debug!("confirm_closure_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})", closure_def_id, @@ -3106,24 +3116,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::Binder(trait_ref) } - fn closure_trait_ref(&mut self, - obligation: &TraitObligation<'tcx>, - closure_def_id: DefId, - substs: ty::ClosureSubsts<'tcx>) - -> Normalized<'tcx, ty::PolyTraitRef<'tcx>> - { - let trait_ref = self.closure_trait_ref_unnormalized( - obligation, closure_def_id, substs); - - // A closure signature can contain associated types which - // must be normalized. - normalize_with_depth(self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth+1, - &trait_ref) - } - fn generator_trait_ref_unnormalized(&mut self, obligation: &TraitObligation<'tcx>, closure_def_id: DefId, @@ -3145,24 +3137,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::Binder(trait_ref) } - fn generator_trait_ref(&mut self, - obligation: &TraitObligation<'tcx>, - closure_def_id: DefId, - substs: ty::ClosureSubsts<'tcx>) - -> Normalized<'tcx, ty::PolyTraitRef<'tcx>> - { - let trait_ref = self.generator_trait_ref_unnormalized( - obligation, closure_def_id, substs); - - // A generator signature can contain associated types which - // must be normalized. - normalize_with_depth(self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth+1, - &trait_ref) - } - /// Returns the obligations that are implied by instantiating an /// impl or trait. The obligations are substituted and fully /// normalized. This is used when confirming an impl or default From de0e695f629c27030584cf5263601fed333f5c0a Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 16 Aug 2017 21:23:44 +0300 Subject: [PATCH 4/5] Check the substituted return type in return type suggestions. Fixes #43420. --- src/librustc_typeck/check/method/mod.rs | 7 + src/librustc_typeck/check/method/probe.rs | 284 ++++++++++-------- src/librustc_typeck/check/method/suggest.rs | 4 + .../issue-43420-no-over-suggest.rs | 19 ++ .../issue-43420-no-over-suggest.stderr | 11 + 5 files changed, 205 insertions(+), 120 deletions(-) create mode 100644 src/test/ui/suggestions/issue-43420-no-over-suggest.rs create mode 100644 src/test/ui/suggestions/issue-43420-no-over-suggest.stderr diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index caf1ab7cf831d..31ceed5b965bf 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -59,6 +59,9 @@ pub enum MethodError<'tcx> { // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have // forgotten to import a trait. IllegalSizedBound(Vec), + + // Found a match, but the return type is wrong + BadReturnType, } // Contains a list of static methods that may apply, a list of unsatisfied trait predicates which @@ -111,6 +114,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Err(Ambiguity(..)) => true, Err(PrivateMatch(..)) => allow_private, Err(IllegalSizedBound(..)) => true, + Err(BadReturnType) => { + bug!("no return type expectations but got BadReturnType") + } + } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index f63db891bfb06..228b6c88f24db 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -32,15 +32,6 @@ use std::rc::Rc; use self::CandidateKind::*; pub use self::PickKind::*; -pub enum LookingFor<'tcx> { - /// looking for methods with the given name; this is the normal case - MethodName(ast::Name), - - /// looking for methods that return a given type; this is used to - /// assemble suggestions - ReturnType(Ty<'tcx>), -} - /// Boolean flag used to indicate if this search is for a suggestion /// or not. If true, we can allow ambiguity and so forth. pub struct IsSuggestion(pub bool); @@ -49,7 +40,8 @@ struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, span: Span, mode: Mode, - looking_for: LookingFor<'tcx>, + method_name: Option, + return_type: Option>, steps: Rc>>, inherent_candidates: Vec>, extension_candidates: Vec>, @@ -84,6 +76,7 @@ struct CandidateStep<'tcx> { #[derive(Debug)] struct Candidate<'tcx> { xform_self_ty: Ty<'tcx>, + xform_ret_ty: Option>, item: ty::AssociatedItem, kind: CandidateKind<'tcx>, import_id: Option, @@ -100,6 +93,13 @@ enum CandidateKind<'tcx> { ty::PolyTraitRef<'tcx>), } +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum ProbeResult { + NoMatch, + BadReturnType, + Match, +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Pick<'tcx> { pub item: ty::AssociatedItem, @@ -175,19 +175,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { return_type, scope_expr_id); let method_names = - self.probe_op(span, mode, LookingFor::ReturnType(return_type), IsSuggestion(true), + self.probe_op(span, mode, None, Some(return_type), IsSuggestion(true), self_ty, scope_expr_id, ProbeScope::TraitsInScope, |probe_cx| Ok(probe_cx.candidate_method_names())) .unwrap_or(vec![]); - method_names - .iter() - .flat_map(|&method_name| { - match self.probe_for_name(span, mode, method_name, IsSuggestion(true), self_ty, - scope_expr_id, ProbeScope::TraitsInScope) { - Ok(pick) => Some(pick.item), - Err(_) => None, - } - }) + method_names + .iter() + .flat_map(|&method_name| { + self.probe_op( + span, mode, Some(method_name), Some(return_type), + IsSuggestion(true), self_ty, scope_expr_id, + ProbeScope::TraitsInScope, |probe_cx| probe_cx.pick() + ).ok().map(|pick| pick.item) + }) .collect() } @@ -206,7 +206,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { scope_expr_id); self.probe_op(span, mode, - LookingFor::MethodName(item_name), + Some(item_name), + None, is_suggestion, self_ty, scope_expr_id, @@ -217,7 +218,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn probe_op(&'a self, span: Span, mode: Mode, - looking_for: LookingFor<'tcx>, + method_name: Option, + return_type: Option>, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: ast::NodeId, @@ -259,7 +261,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // that we create during the probe process are removed later self.probe(|_| { let mut probe_cx = - ProbeContext::new(self, span, mode, looking_for, steps); + ProbeContext::new(self, span, mode, method_name, return_type, steps); probe_cx.assemble_inherent_candidates(); match scope { @@ -329,14 +331,16 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, span: Span, mode: Mode, - looking_for: LookingFor<'tcx>, + method_name: Option, + return_type: Option>, steps: Vec>) -> ProbeContext<'a, 'gcx, 'tcx> { ProbeContext { fcx, span, mode, - looking_for, + method_name, + return_type, inherent_candidates: Vec::new(), extension_candidates: Vec::new(), impl_dups: FxHashSet(), @@ -358,33 +362,25 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { /////////////////////////////////////////////////////////////////////////// // CANDIDATE ASSEMBLY - fn push_inherent_candidate(&mut self, xform_self_ty: Ty<'tcx>, item: ty::AssociatedItem, - kind: CandidateKind<'tcx>, import_id: Option) { - let is_accessible = if let LookingFor::MethodName(name) = self.looking_for { - let def_scope = self.tcx.adjust(name, item.container.id(), self.body_id).1; - item.vis.is_accessible_from(def_scope, self.tcx) - } else { - true - }; - if is_accessible { - self.inherent_candidates.push(Candidate { xform_self_ty, item, kind, import_id }); - } else if self.private_candidate.is_none() { - self.private_candidate = Some(item.def()); - } - } - - fn push_extension_candidate(&mut self, xform_self_ty: Ty<'tcx>, item: ty::AssociatedItem, - kind: CandidateKind<'tcx>, import_id: Option) { - let is_accessible = if let LookingFor::MethodName(name) = self.looking_for { + fn push_candidate(&mut self, + candidate: Candidate<'tcx>, + is_inherent: bool) + { + let is_accessible = if let Some(name) = self.method_name { + let item = candidate.item; let def_scope = self.tcx.adjust(name, item.container.id(), self.body_id).1; item.vis.is_accessible_from(def_scope, self.tcx) } else { true }; if is_accessible { - self.extension_candidates.push(Candidate { xform_self_ty, item, kind, import_id }); + if is_inherent { + self.inherent_candidates.push(candidate); + } else { + self.extension_candidates.push(candidate); + } } else if self.private_candidate.is_none() { - self.private_candidate = Some(item.def()); + self.private_candidate = Some(candidate.item.def()); } } @@ -522,19 +518,22 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let impl_ty = impl_ty.subst(self.tcx, impl_substs); // Determine the receiver type that the method itself expects. - let xform_self_ty = self.xform_self_ty(&item, impl_ty, impl_substs); + let xform_tys = self.xform_self_ty(&item, impl_ty, impl_substs); // We can't use normalize_associated_types_in as it will pollute the // fcx's fulfillment context after this probe is over. let cause = traits::ObligationCause::misc(self.span, self.body_id); let selcx = &mut traits::SelectionContext::new(self.fcx); - let traits::Normalized { value: xform_self_ty, obligations } = - traits::normalize(selcx, self.param_env, cause, &xform_self_ty); - debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}", - xform_self_ty); - - self.push_inherent_candidate(xform_self_ty, item, - InherentImplCandidate(impl_substs, obligations), None); + let traits::Normalized { value: (xform_self_ty, xform_ret_ty), obligations } = + traits::normalize(selcx, self.param_env, cause, &xform_tys); + debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}/{:?}", + xform_self_ty, xform_ret_ty); + + self.push_candidate(Candidate { + xform_self_ty, xform_ret_ty, item, + kind: InherentImplCandidate(impl_substs, obligations), + import_id: None + }, true); } } @@ -555,10 +554,13 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self.elaborate_bounds(&[trait_ref], |this, new_trait_ref, item| { let new_trait_ref = this.erase_late_bound_regions(&new_trait_ref); - let xform_self_ty = + let (xform_self_ty, xform_ret_ty) = this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); - - this.push_inherent_candidate(xform_self_ty, item, ObjectCandidate, None); + this.push_candidate(Candidate { + xform_self_ty, xform_ret_ty, item, + kind: ObjectCandidate, + import_id: None + }, true); }); } @@ -595,7 +597,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self.elaborate_bounds(&bounds, |this, poly_trait_ref, item| { let trait_ref = this.erase_late_bound_regions(&poly_trait_ref); - let xform_self_ty = this.xform_self_ty(&item, trait_ref.self_ty(), trait_ref.substs); + let (xform_self_ty, xform_ret_ty) = + this.xform_self_ty(&item, trait_ref.self_ty(), trait_ref.substs); // Because this trait derives from a where-clause, it // should not contain any inference variables or other @@ -604,8 +607,11 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // `WhereClausePick`. assert!(!trait_ref.substs.needs_infer()); - this.push_inherent_candidate(xform_self_ty, item, - WhereClauseCandidate(poly_trait_ref), None); + this.push_candidate(Candidate { + xform_self_ty, xform_ret_ty, item, + kind: WhereClauseCandidate(poly_trait_ref), + import_id: None + }, true); }); } @@ -658,17 +664,27 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { Ok(()) } - pub fn matches_return_type(&self, method: &ty::AssociatedItem, - expected: ty::Ty<'tcx>) -> bool { + pub fn matches_return_type(&self, + method: &ty::AssociatedItem, + self_ty: Option>, + expected: Ty<'tcx>) -> bool { match method.def() { Def::Method(def_id) => { let fty = self.tcx.fn_sig(def_id); self.probe(|_| { let substs = self.fresh_substs_for_item(self.span, method.def_id); - let output = fty.output().subst(self.tcx, substs); - let (output, _) = self.replace_late_bound_regions_with_fresh_var( - self.span, infer::FnCall, &output); - self.can_sub(self.param_env, output, expected).is_ok() + let fty = fty.subst(self.tcx, substs); + let (fty, _) = self.replace_late_bound_regions_with_fresh_var( + self.span, infer::FnCall, &fty); + + if let Some(self_ty) = self_ty { + if let Err(_) = self.at(&ObligationCause::dummy(), self.param_env) + .sup(fty.inputs()[0], self_ty) + { + return false + } + } + self.can_sub(self.param_env, fty.output(), expected).is_ok() }) } _ => false, @@ -692,23 +708,31 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { continue; } - let xform_self_ty = self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs); - self.push_extension_candidate(xform_self_ty, item, - TraitCandidate(trait_ref), - import_id); + let (xform_self_ty, xform_ret_ty) = + self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs); + self.push_candidate(Candidate { + xform_self_ty, xform_ret_ty, item, import_id, + kind: TraitCandidate(trait_ref), + }, false); } Ok(()) } fn candidate_method_names(&self) -> Vec { let mut set = FxHashSet(); - let mut names: Vec<_> = - self.inherent_candidates - .iter() - .chain(&self.extension_candidates) - .map(|candidate| candidate.item.name) - .filter(|&name| set.insert(name)) - .collect(); + let mut names: Vec<_> = self.inherent_candidates + .iter() + .chain(&self.extension_candidates) + .filter(|candidate| { + if let Some(return_ty) = self.return_type { + self.matches_return_type(&candidate.item, None, return_ty) + } else { + true + } + }) + .map(|candidate| candidate.item.name) + .filter(|&name| set.insert(name)) + .collect(); // sort them by the name so we have a stable result names.sort_by_key(|n| n.as_str()); @@ -719,10 +743,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // THE ACTUAL SEARCH fn pick(mut self) -> PickResult<'tcx> { - assert!(match self.looking_for { - LookingFor::MethodName(_) => true, - LookingFor::ReturnType(_) => false, - }); + assert!(self.method_name.is_some()); if let Some(r) = self.pick_core() { return r; @@ -877,7 +898,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { possibly_unsatisfied_predicates: &mut Vec>) -> Option> { let mut applicable_candidates: Vec<_> = probes.iter() - .filter(|&probe| self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) + .map(|probe| { + (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) + }) + .filter(|&(_, status)| status != ProbeResult::NoMatch) .collect(); debug!("applicable_candidates: {:?}", applicable_candidates); @@ -898,7 +922,13 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { return Some(Err(MethodError::Ambiguity(sources))); } - applicable_candidates.pop().map(|probe| Ok(probe.to_unadjusted_pick())) + applicable_candidates.pop().map(|(probe, status)| { + if status == ProbeResult::Match { + Ok(probe.to_unadjusted_pick()) + } else { + Err(MethodError::BadReturnType) + } + }) } fn select_trait_candidate(&self, trait_ref: ty::TraitRef<'tcx>) @@ -939,7 +969,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self_ty: Ty<'tcx>, probe: &Candidate<'tcx>, possibly_unsatisfied_predicates: &mut Vec>) - -> bool { + -> ProbeResult { debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe); self.probe(|_| { @@ -949,11 +979,11 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { Ok(InferOk { obligations, value: () }) => obligations, Err(_) => { debug!("--> cannot relate self-types"); - return false; + return ProbeResult::NoMatch; } }; - let mut successful = true; + let mut result = ProbeResult::Match; let selcx = &mut traits::SelectionContext::new(self); let cause = traits::ObligationCause::misc(self.span, self.body_id); @@ -996,7 +1026,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // This candidate's primary obligation doesn't even // select - don't bother registering anything in // `potentially_unsatisfied_predicates`. - return false + return ProbeResult::NoMatch; } else { // Some nested subobligation of this predicate // failed. @@ -1004,7 +1034,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // FIXME: try to find the exact nested subobligation // and point at it rather than reporting the entire // trait-ref? - successful = false; + result = ProbeResult::NoMatch; let trait_ref = self.resolve_type_vars_if_possible(&trait_ref); possibly_unsatisfied_predicates.push(trait_ref); } @@ -1020,13 +1050,31 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { for o in candidate_obligations.into_iter().chain(sub_obligations) { let o = self.resolve_type_vars_if_possible(&o); if !selcx.evaluate_obligation(&o) { - successful = false; + result = ProbeResult::NoMatch; if let &ty::Predicate::Trait(ref pred) = &o.predicate { possibly_unsatisfied_predicates.push(pred.0.trait_ref); } } } - successful + + if let ProbeResult::Match = result { + if let (Some(return_ty), Some(xform_ret_ty)) = + (self.return_type, probe.xform_ret_ty) + { + let xform_ret_ty = self.resolve_type_vars_if_possible(&xform_ret_ty); + debug!("comparing return_ty {:?} with xform ret ty {:?}", + return_ty, + probe.xform_ret_ty); + if self.at(&ObligationCause::dummy(), self.param_env) + .sup(return_ty, xform_ret_ty) + .is_err() + { + return ProbeResult::BadReturnType; + } + } + } + + result }) } @@ -1047,22 +1095,25 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { /// /// Now imagine the receiver is `Vec<_>`. It doesn't really matter at this time which impl we /// use, so it's ok to just commit to "using the method from the trait Foo". - fn collapse_candidates_to_trait_pick(&self, probes: &[&Candidate<'tcx>]) -> Option> { + fn collapse_candidates_to_trait_pick(&self, probes: &[(&Candidate<'tcx>, ProbeResult)]) + -> Option> + { // Do all probes correspond to the same trait? - let container = probes[0].item.container; + let container = probes[0].0.item.container; match container { ty::TraitContainer(_) => {} ty::ImplContainer(_) => return None, } - if probes[1..].iter().any(|p| p.item.container != container) { + if probes[1..].iter().any(|&(p, _)| p.item.container != container) { return None; } + // FIXME: check the return type here somehow. // If so, just use this trait and call it a day. Some(Pick { - item: probes[0].item.clone(), + item: probes[0].0.item.clone(), kind: TraitPick, - import_id: probes[0].import_id, + import_id: probes[0].0.import_id, autoderefs: 0, autoref: None, unsize: None, @@ -1100,23 +1151,23 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { item: &ty::AssociatedItem, impl_ty: Ty<'tcx>, substs: &Substs<'tcx>) - -> Ty<'tcx> { + -> (Ty<'tcx>, Option>) { if item.kind == ty::AssociatedKind::Method && self.mode == Mode::MethodCall { - self.xform_method_self_ty(item.def_id, impl_ty, substs) + let sig = self.xform_method_sig(item.def_id, substs); + (sig.inputs()[0], Some(sig.output())) } else { - impl_ty + (impl_ty, None) } } - fn xform_method_self_ty(&self, - method: DefId, - impl_ty: Ty<'tcx>, - substs: &Substs<'tcx>) - -> Ty<'tcx> { - let self_ty = self.tcx.fn_sig(method).input(0); - debug!("xform_self_ty(impl_ty={:?}, self_ty={:?}, substs={:?})", - impl_ty, - self_ty, + fn xform_method_sig(&self, + method: DefId, + substs: &Substs<'tcx>) + -> ty::FnSig<'tcx> + { + let fn_sig = self.tcx.fn_sig(method); + debug!("xform_self_ty(fn_sig={:?}, substs={:?})", + fn_sig, substs); assert!(!substs.has_escaping_regions()); @@ -1132,10 +1183,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // Erase any late-bound regions from the method and substitute // in the values from the substitution. - let xform_self_ty = self.erase_late_bound_regions(&self_ty); + let xform_fn_sig = self.erase_late_bound_regions(&fn_sig); if generics.types.is_empty() && generics.regions.is_empty() { - xform_self_ty.subst(self.tcx, substs) + xform_fn_sig.subst(self.tcx, substs) } else { let substs = Substs::for_item(self.tcx, method, |def, _| { let i = def.index as usize; @@ -1154,7 +1205,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self.type_var_for_def(self.span, def, cur_substs) } }); - xform_self_ty.subst(self.tcx, substs) + xform_fn_sig.subst(self.tcx, substs) } } @@ -1198,17 +1249,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { /// Find the method with the appropriate name (or return type, as the case may be). fn impl_or_trait_item(&self, def_id: DefId) -> Vec { - match self.looking_for { - LookingFor::MethodName(name) => { - self.fcx.associated_item(def_id, name).map_or(Vec::new(), |x| vec![x]) - } - LookingFor::ReturnType(return_ty) => { - self.tcx - .associated_items(def_id) - .map(|did| self.tcx.associated_item(did.def_id)) - .filter(|m| self.matches_return_type(m, return_ty)) - .collect() - } + if let Some(name) = self.method_name { + self.fcx.associated_item(def_id, name).map_or(Vec::new(), |x| vec![x]) + } else { + self.tcx.associated_items(def_id).collect() } } } diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 9079ba4a89382..f2d7842e473f6 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -321,6 +321,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } err.emit(); } + + MethodError::BadReturnType => { + bug!("no return type expectations but got BadReturnType") + } } } diff --git a/src/test/ui/suggestions/issue-43420-no-over-suggest.rs b/src/test/ui/suggestions/issue-43420-no-over-suggest.rs new file mode 100644 index 0000000000000..d504b7cae28c4 --- /dev/null +++ b/src/test/ui/suggestions/issue-43420-no-over-suggest.rs @@ -0,0 +1,19 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// check that we substitute type parameters before we suggest anything - otherwise +// we would suggest function such as `as_slice` for the `&[u16]`. + +fn foo(b: &[u16]) {} + +fn main() { + let a: Vec = Vec::new(); + foo(&a); +} diff --git a/src/test/ui/suggestions/issue-43420-no-over-suggest.stderr b/src/test/ui/suggestions/issue-43420-no-over-suggest.stderr new file mode 100644 index 0000000000000..bcad9ce56c65e --- /dev/null +++ b/src/test/ui/suggestions/issue-43420-no-over-suggest.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/issue-43420-no-over-suggest.rs:18:9 + | +18 | foo(&a); + | ^^ expected slice, found struct `std::vec::Vec` + | + = note: expected type `&[u16]` + found type `&std::vec::Vec` + +error: aborting due to previous error + From 15f6540ec02a6e1c556f0a915c494d8a45ea9338 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 21 Aug 2017 16:44:42 +0300 Subject: [PATCH 5/5] resolve type vars with obligations in more places This fixes a few cases of inference misses, some of them regressions caused by the impl selected for a method not being immediately evaluated. --- src/librustc_typeck/check/mod.rs | 2 + ...thod-argument-inference-associated-type.rs | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/test/run-pass/method-argument-inference-associated-type.rs diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 0427727269607..e937770bb0161 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2818,6 +2818,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { formal_ret: Ty<'tcx>, formal_args: &[Ty<'tcx>]) -> Vec> { + let formal_ret = self.resolve_type_vars_with_obligations(formal_ret); let expected_args = expected_ret.only_has_type(self).and_then(|ret_ty| { self.fudge_regions_if_ok(&RegionVariableOrigin::Coercion(call_span), || { // Attempt to apply a subtyping relationship between the formal @@ -3978,6 +3979,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } hir::ExprTup(ref elts) => { let flds = expected.only_has_type(self).and_then(|ty| { + let ty = self.resolve_type_vars_with_obligations(ty); match ty.sty { ty::TyTuple(ref flds, _) => Some(&flds[..]), _ => None diff --git a/src/test/run-pass/method-argument-inference-associated-type.rs b/src/test/run-pass/method-argument-inference-associated-type.rs new file mode 100644 index 0000000000000..76b8cf92329d1 --- /dev/null +++ b/src/test/run-pass/method-argument-inference-associated-type.rs @@ -0,0 +1,37 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct ClientMap; +pub struct ClientMap2; + +pub trait Service { + type Request; + fn call(&self, _req: Self::Request); +} + +pub struct S(T); + +impl Service for ClientMap { + type Request = S>; + fn call(&self, _req: Self::Request) {} +} + + +impl Service for ClientMap2 { + type Request = (Box,); + fn call(&self, _req: Self::Request) {} +} + + +fn main() { + ClientMap.call(S { 0: Box::new(|_msgid| ()) }); + ClientMap.call(S(Box::new(|_msgid| ()))); + ClientMap2.call((Box::new(|_msgid| ()),)); +}