/// for the object type `Foo`.
#[derive(PartialEq,Eq,Clone)]
pub struct VtableObjectData<'tcx> {
- /// the object type `Foo`.
- pub object_ty: Ty<'tcx>,
-
/// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`.
pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,
+
+ /// The vtable is formed by concatenating together the method lists of
+ /// the base object trait and all supertraits; this is the start of
+ /// `upcast_trait_ref`'s methods in that vtable.
+ pub vtable_base: usize
}
/// Creates predicate obligations from the generic bounds.
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
obligation_trait_ref: &ty::TraitRef<'tcx>,
- candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
- object_ty: Ty<'tcx>)
+ candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
{
+ let self_ty = obligation_trait_ref.self_ty();
+ let object_ty = selcx.infcx().shallow_resolve(self_ty);
debug!("assemble_candidates_from_object_type(object_ty={:?})",
object_ty);
let data = match object_ty.sty {
candidate_set.vec.push(
ProjectionTyCandidate::Impl(data));
}
- super::VtableObject(data) => {
+ super::VtableObject(_) => {
assemble_candidates_from_object_type(
- selcx, obligation, obligation_trait_ref, candidate_set,
- data.object_ty);
+ selcx, obligation, obligation_trait_ref, candidate_set);
}
super::VtableClosure(data) => {
candidate_set.vec.push(
debug!("assemble_candidates_from_object_ty: poly_trait_ref={:?}",
poly_trait_ref);
- // see whether the object trait can be upcast to the trait we are looking for
- let upcast_trait_refs = self.upcast(poly_trait_ref, obligation);
- if upcast_trait_refs.len() > 1 {
+ // Count only those upcast versions that match the trait-ref
+ // we are looking for. Specifically, do not only check for the
+ // correct trait, but also the correct type parameters.
+ // For example, we may be trying to upcast `Foo` to `Bar<i32>`,
+ // but `Foo` is declared as `trait Foo : Bar<u32>`.
+ let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
+ .filter(|upcast_trait_ref| self.infcx.probe(|_| {
+ let upcast_trait_ref = upcast_trait_ref.clone();
+ self.match_poly_trait_ref(obligation, upcast_trait_ref).is_ok()
+ })).count();
+
+ if upcast_trait_refs > 1 {
// can be upcast in many ways; need more type information
candidates.ambiguous = true;
- } else if upcast_trait_refs.len() == 1 {
+ } else if upcast_trait_refs == 1 {
candidates.vec.push(ObjectCandidate);
}
// be exactly one applicable trait-reference; if this were not
// the case, we would have reported an ambiguity error rather
// than successfully selecting one of the candidates.
- let upcast_trait_refs = self.upcast(poly_trait_ref.clone(), obligation);
- assert_eq!(upcast_trait_refs.len(), 1);
- let upcast_trait_ref = upcast_trait_refs.into_iter().next().unwrap();
+ let mut upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
+ .map(|upcast_trait_ref| {
+ (upcast_trait_ref.clone(), self.infcx.probe(|_| {
+ self.match_poly_trait_ref(obligation, upcast_trait_ref)
+ }).is_ok())
+ });
+ let mut upcast_trait_ref = None;
+ let mut vtable_base = 0;
- match self.match_poly_trait_ref(obligation, upcast_trait_ref.clone()) {
- Ok(()) => { }
- Err(()) => {
- self.tcx().sess.span_bug(obligation.cause.span,
- "failed to match trait refs");
+ while let Some((supertrait, matches)) = upcast_trait_refs.next() {
+ if matches {
+ upcast_trait_ref = Some(supertrait);
+ break;
}
+ vtable_base += util::count_own_vtable_entries(self.tcx(), supertrait);
}
+ assert!(upcast_trait_refs.all(|(_, matches)| !matches));
- VtableObjectData { object_ty: self_ty,
- upcast_trait_ref: upcast_trait_ref }
+ VtableObjectData {
+ upcast_trait_ref: upcast_trait_ref.unwrap(),
+ vtable_base: vtable_base
+ }
}
fn confirm_fn_pointer_candidate(&mut self,
/// Returns `Ok` if `poly_trait_ref` being true implies that the
/// obligation is satisfied.
- fn match_poly_trait_ref(&mut self,
+ fn match_poly_trait_ref(&self,
obligation: &TraitObligation<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>)
-> Result<(),()>
obligation.cause.clone()
}
}
-
- /// Upcasts an object trait-reference into those that match the obligation.
- fn upcast(&mut self, obj_trait_ref: ty::PolyTraitRef<'tcx>, obligation: &TraitObligation<'tcx>)
- -> Vec<ty::PolyTraitRef<'tcx>>
- {
- debug!("upcast(obj_trait_ref={:?}, obligation={:?})",
- obj_trait_ref,
- obligation);
-
- let obligation_def_id = obligation.predicate.def_id();
- let mut upcast_trait_refs = util::upcast(self.tcx(), obj_trait_ref, obligation_def_id);
-
- // Retain only those upcast versions that match the trait-ref
- // we are looking for. In particular, we know that all of
- // `upcast_trait_refs` apply to the correct trait, but
- // possibly with incorrect type parameters. For example, we
- // may be trying to upcast `Foo` to `Bar<i32>`, but `Foo` is
- // declared as `trait Foo : Bar<u32>`.
- upcast_trait_refs.retain(|upcast_trait_ref| {
- let upcast_trait_ref = upcast_trait_ref.clone();
- self.infcx.probe(|_| self.match_poly_trait_ref(obligation, upcast_trait_ref)).is_ok()
- });
-
- debug!("upcast: upcast_trait_refs={:?}", upcast_trait_refs);
- upcast_trait_refs
- }
}
impl<'tcx> SelectionCache<'tcx> {
.collect()
}
-/// Given an object of type `object_trait_ref`, returns the index of
-/// the method `method_def_id` (which should be part of a supertrait
-/// of `object_trait_ref`) within the vtable for `object_trait_ref`.
-pub fn get_vtable_index_of_object_method<'tcx>(tcx: &ty::ctxt<'tcx>,
- object_trait_ref: ty::PolyTraitRef<'tcx>,
- method_def_id: ast::DefId) -> usize {
- // We need to figure the "real index" of the method in a
- // listing of all the methods of an object. We do this by
- // iterating down the supertraits of the object's trait until
- // we find the trait the method came from, counting up the
- // methods from them.
- let mut method_count = 0;
-
- let trait_def_id = tcx.impl_or_trait_item(method_def_id).container().id();
-
- for bound_ref in transitive_bounds(tcx, &[object_trait_ref]) {
- if bound_ref.def_id() == trait_def_id {
- break;
- }
-
- let trait_items = tcx.trait_items(bound_ref.def_id());
- for trait_item in trait_items.iter() {
- match *trait_item {
- ty::MethodTraitItem(_) => method_count += 1,
- _ => {}
- }
+/// Given an trait `trait_ref`, returns the number of vtable entries
+/// that come from `trait_ref`, excluding its supertraits. Used in
+/// computing the vtable base for an upcast trait of a trait object.
+pub fn count_own_vtable_entries<'tcx>(tcx: &ty::ctxt<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>)
+ -> usize {
+ let mut entries = 0;
+ // Count number of methods and add them to the total offset.
+ // Skip over associated types and constants.
+ for trait_item in &tcx.trait_items(trait_ref.def_id())[..] {
+ if let ty::MethodTraitItem(_) = *trait_item {
+ entries += 1;
}
}
+ entries
+}
- // count number of methods preceding the one we are selecting and
- // add them to the total offset; skip over associated types.
- for trait_item in &tcx.trait_items(trait_def_id)[..] {
+/// Given an upcast trait object described by `object`, returns the
+/// index of the method `method_def_id` (which should be part of
+/// `object.upcast_trait_ref`) within the vtable for `object`.
+pub fn get_vtable_index_of_object_method<'tcx>(tcx: &ty::ctxt<'tcx>,
+ object: &super::VtableObjectData<'tcx>,
+ method_def_id: ast::DefId) -> usize {
+ // Count number of methods preceding the one we are selecting and
+ // add them to the total offset.
+ // Skip over associated types and constants.
+ let mut entries = object.vtable_base;
+ for trait_item in &tcx.trait_items(object.upcast_trait_ref.def_id())[..] {
if trait_item.def_id() == method_def_id {
// The item with the ID we were given really ought to be a method.
assert!(match *trait_item {
_ => false
});
- return method_count;
+ return entries;
}
- match *trait_item {
- ty::MethodTraitItem(_) => method_count += 1,
- _ => {}
+ if let ty::MethodTraitItem(_) = *trait_item {
+ entries += 1;
}
}
write!(f, "VtableFnPointer({:?})", d),
super::VtableObject(ref d) =>
- write!(f, "VtableObject({:?})", d),
+ write!(f, "{:?}", d),
super::VtableParam(ref n) =>
write!(f, "VtableParam({:?})", n),
impl<'tcx> fmt::Debug for super::VtableObjectData<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "VtableObject(object_ty={:?})", self.object_ty)
+ write!(f, "VtableObject(upcast={:?}, vtable_base={})",
+ self.upcast_trait_ref,
+ self.vtable_base)
}
}
impl<'tcx> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx> {
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableObjectData<'tcx> {
traits::VtableObjectData {
- object_ty: self.object_ty.fold_with(folder),
upcast_trait_ref: self.upcast_trait_ref.fold_with(folder),
+ vtable_base: self.vtable_base
}
}
}
}
}
-fn method_offset_in_object_vtable<'tcx>(tcx: &ty::ctxt<'tcx>,
- object_ty: Ty<'tcx>,
- method_id: ast::DefId)
- -> usize {
- if let ty::TyTrait(ref data) = object_ty.sty {
- let trait_ref = data.principal_trait_ref_with_self_ty(tcx, object_ty);
- traits::get_vtable_index_of_object_method(tcx, trait_ref, method_id)
- } else {
- tcx.sess.bug(&format!(
- "trans::methd::object_ty_to_trait_ref() called on non-object: {:?}",
- object_ty));
- }
-}
-
pub fn trans_static_method_callee<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
method_id: ast::DefId,
trait_id: ast::DefId,
callee_substs)
}
traits::VtableObject(ref data) => {
- let idx = method_offset_in_object_vtable(tcx, data.object_ty, method_id);
+ let idx = traits::get_vtable_index_of_object_method(tcx, data, method_id);
trans_object_shim(ccx,
data.upcast_trait_ref.clone(),
method_id,
}
}
traits::VtableObject(ref data) => {
- let idx = method_offset_in_object_vtable(bcx.tcx(), data.object_ty, method_id);
+ let idx = traits::get_vtable_index_of_object_method(bcx.tcx(), data, method_id);
if let Some(self_expr) = self_expr {
if let ty::TyBareFn(_, ref fty) = monomorphize_type(bcx, method_ty).sty {
let ty = bcx.tcx().mk_fn(None, opaque_method_ty(bcx.tcx(), fty));
--- /dev/null
+// Copyright 2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that the right implementation is called through a trait
+// object when supertraits include multiple references to the
+// same trait, with different type parameters.
+
+trait A: PartialEq<Foo> + PartialEq<Bar> { }
+
+struct Foo;
+struct Bar;
+
+struct Aimpl;
+
+impl PartialEq<Foo> for Aimpl {
+ fn eq(&self, _rhs: &Foo) -> bool {
+ true
+ }
+}
+
+impl PartialEq<Bar> for Aimpl {
+ fn eq(&self, _rhs: &Bar) -> bool {
+ false
+ }
+}
+
+impl A for Aimpl { }
+
+fn main() {
+ let a = &Aimpl as &A;
+
+ assert!(*a == Foo);
+}