]> git.lizzy.rs Git - rust.git/commitdiff
rustc: compute the vtable base of a supertrait during selection. Fixes #26339.
authorEduard Burtescu <edy.burt@gmail.com>
Sat, 4 Jul 2015 02:46:54 +0000 (05:46 +0300)
committerEduard Burtescu <edy.burt@gmail.com>
Sat, 4 Jul 2015 14:51:30 +0000 (17:51 +0300)
src/librustc/middle/traits/mod.rs
src/librustc/middle/traits/project.rs
src/librustc/middle/traits/select.rs
src/librustc/middle/traits/util.rs
src/librustc/middle/ty_fold.rs
src/librustc_trans/trans/meth.rs
src/test/run-pass/traits-issue-26339.rs [new file with mode: 0644]

index 5126a549887eda690c5236bc4c794b701609aa64..b5f01ada7e1789d4467eb62ee88c06564c5c9d3d 100644 (file)
@@ -291,11 +291,13 @@ pub struct VtableBuiltinData<N> {
 /// 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.
index e5f7542aa84c9a12ccf96b968c18c99fd5bb8f60..e29d9646509f9eac17bbe26a44620d8628e6143b 100644 (file)
@@ -629,9 +629,10 @@ fn assemble_candidates_from_object_type<'cx,'tcx>(
     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 {
@@ -684,10 +685,9 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
             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(
index abc300869adcafb61e329c21de4aecb4e67510b5..ad91f664af2db6ac7289cb2336be3eab7cefbb9f 100644 (file)
@@ -1362,12 +1362,21 @@ fn assemble_candidates_from_object_ty(&mut self,
             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);
             }
 
@@ -2305,20 +2314,28 @@ fn confirm_object_candidate(&mut self,
         // 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,
@@ -2719,7 +2736,7 @@ fn match_where_clause_trait_ref(&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<(),()>
@@ -2930,32 +2947,6 @@ fn derived_cause(&self,
             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> {
index e8a24876b18dcd9f4df8b1f2ef8bf388435a5a86..af9d5e5157d28e8567b3ecf2a72602e208184cbb 100644 (file)
@@ -396,38 +396,34 @@ pub fn upcast<'tcx>(tcx: &ty::ctxt<'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 {
@@ -435,11 +431,10 @@ pub fn get_vtable_index_of_object_method<'tcx>(tcx: &ty::ctxt<'tcx>,
                 _ => false
             });
 
-            return method_count;
+            return entries;
         }
-        match *trait_item {
-            ty::MethodTraitItem(_) => method_count += 1,
-            _ => {}
+        if let ty::MethodTraitItem(_) = *trait_item {
+            entries += 1;
         }
     }
 
@@ -493,7 +488,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                 write!(f, "VtableFnPointer({:?})", d),
 
             super::VtableObject(ref d) =>
-                write!(f, "VtableObject({:?})", d),
+                write!(f, "{:?}", d),
 
             super::VtableParam(ref n) =>
                 write!(f, "VtableParam({:?})", n),
@@ -538,7 +533,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 
 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)
     }
 }
 
index 402f31fc770d909e010b98b6061215d2b2d2c3b9..7016c1484659b7165da31aa66a5a778434824852 100644 (file)
@@ -492,8 +492,8 @@ fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::Vtable<'tcx,
 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
         }
     }
 }
index ece5682c6ba9bc30459552afa422a7231412c8c6..2cab7d2a1c84a396af6de90326687ca9901e6219 100644 (file)
@@ -160,20 +160,6 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 }
 
-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,
@@ -285,7 +271,7 @@ pub fn trans_static_method_callee<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                      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,
@@ -384,7 +370,7 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             }
         }
         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));
diff --git a/src/test/run-pass/traits-issue-26339.rs b/src/test/run-pass/traits-issue-26339.rs
new file mode 100644 (file)
index 0000000..f2cf177
--- /dev/null
@@ -0,0 +1,40 @@
+// 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);
+}