]> git.lizzy.rs Git - rust.git/commitdiff
Implement default method inheritance.
authorAaron Turon <aturon@mozilla.com>
Mon, 28 Dec 2015 23:38:26 +0000 (15:38 -0800)
committerAaron Turon <aturon@mozilla.com>
Mon, 14 Mar 2016 22:04:36 +0000 (15:04 -0700)
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
src/librustc/middle/traits/mod.rs
src/librustc/middle/traits/select.rs
src/librustc/middle/traits/specialize.rs
src/librustc/middle/ty/mod.rs
src/librustc/middle/ty/trait_def.rs
src/librustc/middle/ty/util.rs
src/librustc_trans/trans/meth.rs
src/librustc_typeck/check/mod.rs

index 00c72d0b48ab051f236cf01141f2edc001134b57..33a1e3816e348ce204d563190bab20ffcf494c90 100644 (file)
@@ -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
index cbb0cc5aed70438ed97aca45386314e206220540..8d3403021e41577f9ab7bd7d5649d73b34a8f839 100644 (file)
@@ -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;
index a5fe1191d3f0ca744f0aac96c2505163d1f90621..d319ac0219c75fa8bc5373ffc4acfffbaa4b11f3 100644 (file)
@@ -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);
                     }
                 }
 
index ad8d6c4f9566120024c86f036d524f88830e9626..b2339b3080c9aa4c15ef7e59666237766e0933df 100644 (file)
@@ -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<I>
+{
+    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()[&current_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
 }
index c336fd558bd563fa119caccc5dbdd3fd2ee36b9f..7fa20f2135ae1d56f790f62b1fe6c7b29f9f513c 100644 (file)
@@ -2719,28 +2719,4 @@ pub fn with_freevars<T, F>(&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<Ty> =
-            method.generics.types.get_slice(subst::FnSpace)
-                  .iter()
-                  .map(|def| self.mk_param_from_def(def))
-                  .collect();
-        let meth_regions: Vec<ty::Region> =
-            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)
-    }
 }
index a67b2d9fb1cfcf540e5d29e8420764da2558edd4..582b289455196fc545c3d62ca0c3eef0eb79f7dc 100644 (file)
@@ -190,8 +190,14 @@ pub fn add_impl_for_specialization(&self,
             .insert(tcx, impl_def_id, impl_trait_ref)
     }
 
-    pub fn for_each_impl<F: FnMut(DefId)>(&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<DefId> {
+        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<F: FnMut(DefId)>(&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() {
index c91441a3f8a4ba6ab21d25b2cce62a40634ca9ac..db28963d2c0c6f03c1595e58c114d0f774ffb643 100644 (file)
@@ -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<ty::Method<'tcx>>,
-    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,
index 78b86dafa18ad83dd31172f1930d6036fb916f5e..073ef9797ed2a46d068c7c68eb4b1a6780714016 100644 (file)
@@ -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<ty::Method<'tcx>>,
+    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))
+    })
+}
index 19731407d9e7a302cb03dc92aace2354d9aad701..136e8c6569b769204c5dea2add7eca088741bded 100644 (file)
@@ -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) => {