mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
rustc: compute the vtable base of a supertrait during selection. Fixes #26339.
This commit is contained in:
parent
96d24a5c58
commit
536e71b78f
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -1362,12 +1362,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
// 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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
|
||||
/// 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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
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> {
|
||||
|
@ -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 @@ impl<'tcx, N:fmt::Debug> fmt::Debug for super::Vtable<'tcx, N> {
|
||||
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 @@ impl<'tcx, N:fmt::Debug> fmt::Debug for super::VtableDefaultImplData<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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,8 +492,8 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
40
src/test/run-pass/traits-issue-26339.rs
Normal file
40
src/test/run-pass/traits-issue-26339.rs
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user