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;
/// 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>,
}
-> 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
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
}
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()[¤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> {
/// 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?
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,
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;
// 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
}