From 7e42a780161e757ddd7d20925691a861f9d86725 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 28 Dec 2015 15:38:26 -0800 Subject: [PATCH] Implement default method inheritance. This commit leverages the specialization graph infrastructure to allow specializing trait implementations to leave off methods for which their parents have provided defaults. It does not yet check that the `default` keyword is appropriately used in such cases. --- src/librustc/middle/traits/coherence.rs | 5 +- src/librustc/middle/traits/mod.rs | 2 +- src/librustc/middle/traits/select.rs | 2 +- src/librustc/middle/traits/specialize.rs | 254 ++++++++++++++++++++--- src/librustc/middle/ty/mod.rs | 24 --- src/librustc/middle/ty/trait_def.rs | 10 +- src/librustc/middle/ty/util.rs | 55 +---- src/librustc_trans/trans/meth.rs | 43 +++- src/librustc_typeck/check/mod.rs | 39 ++-- 9 files changed, 296 insertions(+), 138 deletions(-) diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 00c72d0b48a..33a1e3816e3 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -85,10 +85,7 @@ fn overlap<'cx, 'tcx>(selcx: &mut SelectionContext<'cx, 'tcx>, return None } - let substituted = selcx.infcx().resolve_type_vars_if_possible(&a_impl_header); - let freshened = selcx.infcx().freshen(substituted); - - Some(freshened) + Some(selcx.infcx().resolve_type_vars_if_possible(&a_impl_header)) } pub fn trait_ref_is_knowable<'tcx>(tcx: &TyCtxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> bool diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index cbb0cc5aed7..8d3403021e4 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -50,7 +50,7 @@ pub use self::select::SelectionCache; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants -pub use self::specialize::{Overlap, SpecializationGraph, specializes}; +pub use self::specialize::{Overlap, SpecializationGraph, get_impl_item_or_default, ItemSource, specializes}; pub use self::util::elaborate_predicates; pub use self::util::get_vtable_index_of_object_method; pub use self::util::trait_ref_for_builtin_bound; diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index a5fe1191d3f..d319ac0219c 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1629,7 +1629,7 @@ fn candidate_should_be_dropped_in_favor_of<'o>( // i.e. EvaluatedToOk: if other.evaluation == EvaluatedToOk { if let ImplCandidate(victim_def) = victim.candidate { - return traits::specializes(self.infcx(), other_def, victim_def); + return traits::specializes(self.tcx(), other_def, victim_def); } } diff --git a/src/librustc/middle/traits/specialize.rs b/src/librustc/middle/traits/specialize.rs index ad8d6c4f956..b2339b3080c 100644 --- a/src/librustc/middle/traits/specialize.rs +++ b/src/librustc/middle/traits/specialize.rs @@ -23,7 +23,7 @@ use middle::region; use middle::subst::{Subst, Substs}; use middle::traits; -use middle::ty; +use middle::ty::{self, ImplOrTraitItem}; use syntax::codemap::DUMMY_SP; use util::nodemap::DefIdMap; @@ -51,6 +51,8 @@ pub struct SpecializationGraph { /// Information pertinent to an overlapping impl error. pub struct Overlap<'tcx> { pub with_impl: DefId, + + /// NB: this TraitRef can contain inference variables! pub on_trait_ref: ty::TraitRef<'tcx>, } @@ -72,9 +74,7 @@ pub fn insert<'tcx>(&mut self, -> Result<(), Overlap<'tcx>> { assert!(impl_def_id.is_local()); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); let mut parent = trait_ref.def_id; - let mut my_children = vec![]; // descend the existing tree, looking for the right location to add this impl @@ -84,13 +84,12 @@ pub fn insert<'tcx>(&mut self, for slot in possible_siblings.iter_mut() { let possible_sibling = *slot; - let overlap = infcx.probe(|_| { - traits::overlapping_impls(&infcx, possible_sibling, impl_def_id) - }); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); + let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); if let Some(trait_ref) = overlap { - let le = specializes(&infcx, impl_def_id, possible_sibling); - let ge = specializes(&infcx, possible_sibling, impl_def_id); + let le = specializes(tcx, impl_def_id, possible_sibling); + let ge = specializes(tcx, possible_sibling, impl_def_id); if le && !ge { // the impl specializes possible_sibling @@ -120,22 +119,184 @@ pub fn insert<'tcx>(&mut self, } if self.children.insert(impl_def_id, my_children).is_some() { - panic!("When inserting an impl into the specialization graph, existing children for \ - the impl were already present."); + tcx.sess + .bug("When inserting an impl into the specialization graph, existing children for \ + the impl were already present."); } Ok(()) } - /// Insert cached metadata mapping from a child impl back to its parent + /// Insert cached metadata mapping from a child impl back to its parent. pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) { - if self.parent.insert(child, Some(parent)).is_some() { + if self.parent.insert(child, parent).is_some() { panic!("When recording an impl from the crate store, information about its parent \ was already present."); } self.children.entry(parent).or_insert(vec![]).push(child); } + + /// The parent of a given impl, which is the def id of the trait when the + /// impl is a "specialization root". + pub fn parent(&self, child: DefId) -> DefId { + *self.parent.get(&child).unwrap() + } +} + +/// When we have selected one impl, but are actually using item definitions from +/// a parent impl providing a default, we need a way to translate between the +/// type parameters of the two impls. Here the `source_impl` is the one we've +/// selected, and `source_substs` is a substitution of its generics (and possibly +/// some relevant `FnSpace` variables as well). And `target_impl` is the impl +/// we're actually going to get the definition from. +fn translate_substs_between_impls<'tcx>(tcx: &ty::ctxt<'tcx>, + source_impl: DefId, + source_substs: Substs<'tcx>, + target_impl: DefId) + -> Substs<'tcx> { + + // We need to build a subst that covers all the generics of + // `target_impl`. Start by introducing fresh infer variables: + let target_generics = tcx.lookup_item_type(target_impl).generics; + let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); + let mut target_substs = infcx.fresh_substs_for_generics(DUMMY_SP, &target_generics); + if source_substs.regions.is_erased() { + target_substs = target_substs.erase_regions() + } + + if !fulfill_implication(&mut infcx, + source_impl, + source_substs.clone(), + target_impl, + target_substs.clone()) { + tcx.sess + .bug("When translating substitutions for specialization, the expected specializaiton \ + failed to hold") + } + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. We + // also carry along any FnSpace substitutions, which don't need to be + // adjusted when mapping from one impl to another. + infcx.resolve_type_vars_if_possible(&target_substs) + .with_method_from_subst(&source_substs) +} + +/// When we've selected an impl but need to use an item definition provided by +/// the trait itself, we need to translate the substitution applied to the impl +/// to one that makes sense for the trait. +fn translate_substs_from_impl_to_trait<'tcx>(tcx: &ty::ctxt<'tcx>, + source_impl: DefId, + source_substs: Substs<'tcx>) + -> Substs<'tcx> { + + let source_trait_ref = tcx.impl_trait_ref(source_impl).unwrap().subst(tcx, &source_substs); + + let mut new_substs = source_trait_ref.substs.clone(); + if source_substs.regions.is_erased() { + new_substs = new_substs.erase_regions() + } + + // Carry any FnSpace substitutions along; they don't need to be adjusted + new_substs.with_method_from_subst(&source_substs) +} + +#[derive(Debug, Copy, Clone)] +/// When looking up an item in an impl, it may turn out that the item +/// is actually provided as a default by a more generic impl, or by +/// the trait itself. This enum says where the item came from. +pub enum ItemSource { + Impl { + requested_impl: DefId, + actual_impl: DefId, + }, + Trait { + requested_impl: DefId, + }, +} + +impl ItemSource { + pub fn is_from_trait(&self) -> bool { + match *self { + ItemSource::Trait { .. } => true, + _ => false, + } + } + + /// Given a subst for the requested impl, translate it to a subst + /// appropriate for the actual item definition (whether it be in that impl, + /// a parent impl, or the trait). + pub fn translate_substs<'tcx>(&self, + tcx: &ty::ctxt<'tcx>, + requested_impl_substs: Substs<'tcx>) + -> Substs<'tcx> { + match *self { + ItemSource::Impl { requested_impl, actual_impl } => { + // no need to translate if we're targetting the impl we started with + if requested_impl == actual_impl { + return requested_impl_substs; + } + + translate_substs_between_impls(tcx, + requested_impl, + requested_impl_substs, + actual_impl) + + } + ItemSource::Trait { requested_impl } => { + translate_substs_from_impl_to_trait(tcx, requested_impl, requested_impl_substs) + } + } + } +} + +/// Lookup the definition of an item within `requested_impl` or its specialization +/// parents, including provided items from the trait itself. +/// +/// The closure `f` works in the style of `filter_map`. +pub fn get_impl_item_or_default<'tcx, I, F>(tcx: &ty::ctxt<'tcx>, + requested_impl: DefId, + mut f: F) + -> Option<(I, ItemSource)> + where F: for<'a> FnMut(&ImplOrTraitItem<'tcx>) -> Option +{ + let impl_or_trait_items_map = tcx.impl_or_trait_items.borrow(); + let trait_def_id = tcx.trait_id_of_impl(requested_impl).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + + // Walk up the specialization tree, looking for a matching item definition + + let mut current_impl = requested_impl; + loop { + for impl_item_id in &tcx.impl_items.borrow()[¤t_impl] { + let impl_item = &impl_or_trait_items_map[&impl_item_id.def_id()]; + if let Some(t) = f(impl_item) { + let source = ItemSource::Impl { + requested_impl: requested_impl, + actual_impl: current_impl, + }; + return Some((t, source)); + } + } + + if let Some(parent) = trait_def.parent_of_impl(current_impl) { + current_impl = parent; + } else { + break; + } + } + + // The item isn't defined anywhere in the hierarchy. Get the + // default from the trait. + + for trait_item in tcx.trait_items(trait_def_id).iter() { + if let Some(t) = f(trait_item) { + return Some((t, ItemSource::Trait { requested_impl: requested_impl })); + } + } + + None } fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Substs<'a> { @@ -155,30 +316,57 @@ fn skolemizing_subst_for_impl<'a>(tcx: &ty::ctxt<'a>, impl_def_id: DefId) -> Sub /// Specialization is determined by the sets of types to which the impls apply; /// impl1 specializes impl2 if it applies to a subset of the types impl2 applies /// to. -pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { - let tcx = &infcx.tcx; - +pub fn specializes(tcx: &ty::ctxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { // We determine whether there's a subset relationship by: // // - skolemizing impl1, + // - instantiating impl2 with fresh inference variables, // - assuming the where clauses for impl1, // - unifying, // - attempting to prove the where clauses for impl2 // + // The last three steps are essentially checking for an implication between two impls + // after appropriate substitutions. This is what `fulfill_implication` checks for. + // // See RFC 1210 for more details and justification. + let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); + let impl1_substs = skolemizing_subst_for_impl(tcx, impl1_def_id); + let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id); + + fulfill_implication(&mut infcx, + impl1_def_id, + impl1_substs, + impl2_def_id, + impl2_substs) +} + +/// Does impl1 (instantiated with the impl1_substs) imply impl2 +/// (instantiated with impl2_substs)? +/// +/// Mutates the `infcx` in two ways: +/// - by adding the obligations of impl1 to the parameter environment +/// - via fulfillment, so that if the implication holds the various unifications +fn fulfill_implication<'a, 'tcx>(infcx: &mut InferCtxt<'a, 'tcx>, + impl1_def_id: DefId, + impl1_substs: Substs<'tcx>, + impl2_def_id: DefId, + impl2_substs: Substs<'tcx>) + -> bool { + let tcx = &infcx.tcx; + let (impl1_trait_ref, impl1_obligations) = { let selcx = &mut SelectionContext::new(&infcx); util::impl_trait_ref_and_oblig(selcx, impl1_def_id, &impl1_substs) }; let impl1_predicates: Vec<_> = impl1_obligations.iter() - .cloned() - .map(|oblig| oblig.predicate) - .collect(); + .cloned() + .map(|oblig| oblig.predicate) + .collect(); - let penv = ty::ParameterEnvironment { + infcx.parameter_environment = ty::ParameterEnvironment { tcx: tcx, free_substs: impl1_substs, implicit_region_bound: ty::ReEmpty, // FIXME: is this OK? @@ -188,13 +376,10 @@ pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) free_id_outlive: region::DUMMY_CODE_EXTENT, // FIXME: is this OK? }; - // FIXME: unclear what `errors_will_be_reported` should be here... - let infcx = infer::new_infer_ctxt(tcx, infcx.tables, Some(penv), true); let selcx = &mut SelectionContext::new(&infcx); - - let impl2_substs = util::fresh_type_vars_for_impl(&infcx, DUMMY_SP, impl2_def_id); - let (impl2_trait_ref, impl2_obligations) = - util::impl_trait_ref_and_oblig(selcx, impl2_def_id, &impl2_substs); + let (impl2_trait_ref, impl2_obligations) = util::impl_trait_ref_and_oblig(selcx, + impl2_def_id, + &impl2_substs); // do the impls unify? If not, no specialization. if let Err(_) = infer::mk_eq_trait_refs(&infcx, @@ -202,7 +387,7 @@ pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) TypeOrigin::Misc(DUMMY_SP), impl1_trait_ref, impl2_trait_ref) { - debug!("specializes: {:?} does not unify with {:?}", + debug!("fulfill_implication: {:?} does not unify with {:?}", impl1_trait_ref, impl2_trait_ref); return false; @@ -212,21 +397,24 @@ pub fn specializes(infcx: &InferCtxt, impl1_def_id: DefId, impl2_def_id: DefId) // attempt to prove all of the predicates for impl2 given those for impl1 // (which are packed up in penv) + for oblig in impl2_obligations.into_iter() { fulfill_cx.register_predicate_obligation(&infcx, oblig); } if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) { - debug!("specializes: for impls on {:?} and {:?}, could not fulfill: {:?} given {:?}", + // no dice! + debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \ + {:?}", impl1_trait_ref, impl2_trait_ref, errors, infcx.parameter_environment.caller_bounds); - return false; + false + } else { + debug!("fulfill_implication: an impl for {:?} specializes {:?} (`where` clauses elided)", + impl1_trait_ref, + impl2_trait_ref); + true } - - debug!("specializes: an impl for {:?} specializes {:?} (`where` clauses elided)", - impl1_trait_ref, - impl2_trait_ref); - true } diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index c336fd558bd..7fa20f2135a 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -2719,28 +2719,4 @@ pub fn with_freevars(&self, fid: NodeId, f: F) -> T where Some(d) => f(&d[..]) } } - - pub fn make_substs_for_receiver_types(&self, - trait_ref: &ty::TraitRef<'tcx>, - method: &ty::Method<'tcx>) - -> subst::Substs<'tcx> - { - /*! - * Substitutes the values for the receiver's type parameters - * that are found in method, leaving the method's type parameters - * intact. - */ - - let meth_tps: Vec = - method.generics.types.get_slice(subst::FnSpace) - .iter() - .map(|def| self.mk_param_from_def(def)) - .collect(); - let meth_regions: Vec = - method.generics.regions.get_slice(subst::FnSpace) - .iter() - .map(|def| def.to_early_bound_region()) - .collect(); - trait_ref.substs.clone().with_method(meth_tps, meth_regions) - } } diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs index a67b2d9fb1c..582b2894551 100644 --- a/src/librustc/middle/ty/trait_def.rs +++ b/src/librustc/middle/ty/trait_def.rs @@ -190,8 +190,14 @@ pub fn add_impl_for_specialization(&self, .insert(tcx, impl_def_id, impl_trait_ref) } - pub fn for_each_impl(&self, tcx: &TyCtxt<'tcx>, mut f: F) { - self.read_trait_impls(tcx); + /// Returns the immediately less specialized impl, if any. + pub fn parent_of_impl(&self, impl_def_id: DefId) -> Option { + let parent = self.specialization_graph.borrow().parent(impl_def_id); + if parent == self.trait_ref.def_id { None } else { Some(parent) } + } + + pub fn for_each_impl(&self, tcx: &TyCtxt<'tcx>, mut f: F) { + self.read_trait_impls(tcx); tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); for &impl_def_id in self.blanket_impls.borrow().iter() { diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs index c91441a3f8a..db28963d2c0 100644 --- a/src/librustc/middle/ty/util.rs +++ b/src/librustc/middle/ty/util.rs @@ -14,7 +14,7 @@ use middle::const_eval::{self, ConstVal, ErrKind}; use middle::const_eval::EvalHint::UncheckedExprHint; use middle::def_id::DefId; -use middle::subst::{self, Subst, Substs}; +use middle::subst; use middle::infer; use middle::pat_util; use middle::traits; @@ -26,7 +26,6 @@ use std::cmp; use std::hash::{Hash, SipHasher, Hasher}; -use std::rc::Rc; use syntax::ast::{self, Name}; use syntax::attr::{self, AttrMetaMethods, SignedInt, UnsignedInt}; use syntax::codemap::Span; @@ -536,58 +535,6 @@ pub fn is_adt_dtorck(&self, adt: ty::AdtDef<'tcx>) -> bool { } } -#[derive(Debug)] -pub struct ImplMethod<'tcx> { - pub method: Rc>, - pub substs: &'tcx Substs<'tcx>, - pub is_provided: bool -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn get_impl_method(&self, - impl_def_id: DefId, - substs: &'tcx Substs<'tcx>, - name: Name) - -> ImplMethod<'tcx> - { - // there don't seem to be nicer accessors to these: - let impl_or_trait_items_map = self.impl_or_trait_items.borrow(); - - for impl_item in &self.impl_items.borrow()[&impl_def_id] { - if let ty::MethodTraitItem(ref meth) = - impl_or_trait_items_map[&impl_item.def_id()] { - if meth.name == name { - return ImplMethod { - method: meth.clone(), - substs: substs, - is_provided: false - } - } - } - } - - // It is not in the impl - get the default from the trait. - let trait_ref = self.impl_trait_ref(impl_def_id).unwrap(); - for trait_item in self.trait_items(trait_ref.def_id).iter() { - if let &ty::MethodTraitItem(ref meth) = trait_item { - if meth.name == name { - let impl_to_trait_substs = self - .make_substs_for_receiver_types(&trait_ref, meth); - let substs = impl_to_trait_substs.subst(self, substs); - return ImplMethod { - method: meth.clone(), - substs: self.mk_substs(substs), - is_provided: true - } - } - } - } - - self.sess.bug(&format!("method {:?} not found in {:?}", - name, impl_def_id)) - } -} - impl<'tcx> ty::TyS<'tcx> { fn impls_bound<'a>(&'tcx self, param_env: &ParameterEnvironment<'a,'tcx>, bound: ty::BuiltinBound, diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 78b86dafa18..073ef9797ed 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::rc::Rc; + use arena::TypedArena; use back::link; use llvm::{ValueRef, get_params}; @@ -33,7 +35,7 @@ use trans::type_of::*; use middle::ty::{self, Ty, TyCtxt}; -use syntax::ast; +use syntax::ast::{self, Name}; use syntax::attr; use syntax::codemap::DUMMY_SP; @@ -107,7 +109,7 @@ pub fn callee_for_trait_impl<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // those from the impl and those from the method: let impl_substs = vtable_impl.substs.with_method_from(&substs); let substs = ccx.tcx().mk_substs(impl_substs); - let mth = ccx.tcx().get_impl_method(impl_did, substs, mname); + let mth = get_impl_method(ccx.tcx(), impl_did, impl_substs, mname); // Translate the function, bypassing Callee::def. // That is because default methods have the same ID as the @@ -428,7 +430,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. - let mth = tcx.get_impl_method(impl_id, substs, name); + let mth = get_impl_method(tcx, impl_id, substs.clone(), name); debug!("get_vtable_methods: mth={:?}", mth); @@ -466,3 +468,38 @@ fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>) }), }) } + +#[derive(Debug)] +pub struct ImplMethod<'tcx> { + pub method: Rc>, + pub substs: Substs<'tcx>, + pub is_provided: bool +} + +/// Locates the applicable definition of a method, given its name. +pub fn get_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, + impl_def_id: DefId, + substs: Substs<'tcx>, + name: Name) + -> ImplMethod<'tcx> +{ + assert!(!substs.types.needs_infer()); + + traits::get_impl_item_or_default(tcx, impl_def_id, |cand| { + if let &ty::MethodTraitItem(ref meth) = cand { + if meth.name == name { + return Some(meth.clone()) + } + } + None + }).map(|(meth, source)| { + ImplMethod { + method: meth, + substs: source.translate_substs(tcx, substs), + is_provided: source.is_from_trait(), + } + }).unwrap_or_else(|| { + tcx.sess.bug(&format!("method {:?} not found in {:?}", + name, impl_def_id)) + }) +} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 19731407d9e..136e8c6569b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -672,10 +672,12 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) { hir::ItemFn(..) => {} // entirely within check_item_body hir::ItemImpl(_, _, _, _, _, ref impl_items) => { debug!("ItemImpl {} with id {}", it.name, it.id); - match ccx.tcx.impl_trait_ref(ccx.tcx.map.local_def_id(it.id)) { + let impl_def_id = ccx.tcx.map.local_def_id(it.id); + match ccx.tcx.impl_trait_ref(impl_def_id) { Some(impl_trait_ref) => { check_impl_items_against_trait(ccx, it.span, + impl_def_id, &impl_trait_ref, impl_items); } @@ -864,6 +866,7 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_span: Span, + impl_id: DefId, impl_trait_ref: &ty::TraitRef<'tcx>, impl_items: &[hir::ImplItem]) { // Locate trait methods @@ -973,23 +976,27 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } } ty::MethodTraitItem(ref trait_method) => { - let is_implemented = - impl_items.iter().any(|ii| { - match ii.node { - hir::ImplItemKind::Method(..) => { - ii.name == trait_method.name - } - _ => false, + let search_result = traits::get_impl_item_or_default(tcx, impl_id, |cand| { + if let &ty::MethodTraitItem(ref meth) = cand { + if meth.name == trait_method.name { + return Some(()); } - }); - let is_provided = - provided_methods.iter().any(|m| m.name == trait_method.name); - if !is_implemented { - if !is_provided { - missing_items.push(trait_method.name); - } else if associated_type_overridden { - invalidated_items.push(trait_method.name); } + None + }); + + if let Some((_, source)) = search_result { + if source.is_from_trait() { + let is_provided = + provided_methods.iter().any(|m| m.name == trait_method.name); + if !is_provided { + missing_items.push(trait_method.name); + } else if associated_type_overridden { + invalidated_items.push(trait_method.name); + } + } + } else { + missing_items.push(trait_method.name); } } ty::TypeTraitItem(ref associated_type) => { -- 2.44.0