Skip to content

Commit 536e71b

Browse files
committed
rustc: compute the vtable base of a supertrait during selection. Fixes #26339.
1 parent 96d24a5 commit 536e71b

File tree

7 files changed

+117
-101
lines changed

7 files changed

+117
-101
lines changed

src/librustc/middle/traits/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,13 @@ pub struct VtableBuiltinData<N> {
291291
/// for the object type `Foo`.
292292
#[derive(PartialEq,Eq,Clone)]
293293
pub struct VtableObjectData<'tcx> {
294-
/// the object type `Foo`.
295-
pub object_ty: Ty<'tcx>,
296-
297294
/// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`.
298295
pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,
296+
297+
/// The vtable is formed by concatenating together the method lists of
298+
/// the base object trait and all supertraits; this is the start of
299+
/// `upcast_trait_ref`'s methods in that vtable.
300+
pub vtable_base: usize
299301
}
300302

301303
/// Creates predicate obligations from the generic bounds.

src/librustc/middle/traits/project.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -629,9 +629,10 @@ fn assemble_candidates_from_object_type<'cx,'tcx>(
629629
selcx: &mut SelectionContext<'cx,'tcx>,
630630
obligation: &ProjectionTyObligation<'tcx>,
631631
obligation_trait_ref: &ty::TraitRef<'tcx>,
632-
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
633-
object_ty: Ty<'tcx>)
632+
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
634633
{
634+
let self_ty = obligation_trait_ref.self_ty();
635+
let object_ty = selcx.infcx().shallow_resolve(self_ty);
635636
debug!("assemble_candidates_from_object_type(object_ty={:?})",
636637
object_ty);
637638
let data = match object_ty.sty {
@@ -684,10 +685,9 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
684685
candidate_set.vec.push(
685686
ProjectionTyCandidate::Impl(data));
686687
}
687-
super::VtableObject(data) => {
688+
super::VtableObject(_) => {
688689
assemble_candidates_from_object_type(
689-
selcx, obligation, obligation_trait_ref, candidate_set,
690-
data.object_ty);
690+
selcx, obligation, obligation_trait_ref, candidate_set);
691691
}
692692
super::VtableClosure(data) => {
693693
candidate_set.vec.push(

src/librustc/middle/traits/select.rs

+32-41
Original file line numberDiff line numberDiff line change
@@ -1362,12 +1362,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13621362
debug!("assemble_candidates_from_object_ty: poly_trait_ref={:?}",
13631363
poly_trait_ref);
13641364

1365-
// see whether the object trait can be upcast to the trait we are looking for
1366-
let upcast_trait_refs = self.upcast(poly_trait_ref, obligation);
1367-
if upcast_trait_refs.len() > 1 {
1365+
// Count only those upcast versions that match the trait-ref
1366+
// we are looking for. Specifically, do not only check for the
1367+
// correct trait, but also the correct type parameters.
1368+
// For example, we may be trying to upcast `Foo` to `Bar<i32>`,
1369+
// but `Foo` is declared as `trait Foo : Bar<u32>`.
1370+
let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
1371+
.filter(|upcast_trait_ref| self.infcx.probe(|_| {
1372+
let upcast_trait_ref = upcast_trait_ref.clone();
1373+
self.match_poly_trait_ref(obligation, upcast_trait_ref).is_ok()
1374+
})).count();
1375+
1376+
if upcast_trait_refs > 1 {
13681377
// can be upcast in many ways; need more type information
13691378
candidates.ambiguous = true;
1370-
} else if upcast_trait_refs.len() == 1 {
1379+
} else if upcast_trait_refs == 1 {
13711380
candidates.vec.push(ObjectCandidate);
13721381
}
13731382

@@ -2305,20 +2314,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
23052314
// be exactly one applicable trait-reference; if this were not
23062315
// the case, we would have reported an ambiguity error rather
23072316
// than successfully selecting one of the candidates.
2308-
let upcast_trait_refs = self.upcast(poly_trait_ref.clone(), obligation);
2309-
assert_eq!(upcast_trait_refs.len(), 1);
2310-
let upcast_trait_ref = upcast_trait_refs.into_iter().next().unwrap();
2317+
let mut upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref)
2318+
.map(|upcast_trait_ref| {
2319+
(upcast_trait_ref.clone(), self.infcx.probe(|_| {
2320+
self.match_poly_trait_ref(obligation, upcast_trait_ref)
2321+
}).is_ok())
2322+
});
2323+
let mut upcast_trait_ref = None;
2324+
let mut vtable_base = 0;
23112325

2312-
match self.match_poly_trait_ref(obligation, upcast_trait_ref.clone()) {
2313-
Ok(()) => { }
2314-
Err(()) => {
2315-
self.tcx().sess.span_bug(obligation.cause.span,
2316-
"failed to match trait refs");
2326+
while let Some((supertrait, matches)) = upcast_trait_refs.next() {
2327+
if matches {
2328+
upcast_trait_ref = Some(supertrait);
2329+
break;
23172330
}
2331+
vtable_base += util::count_own_vtable_entries(self.tcx(), supertrait);
23182332
}
2333+
assert!(upcast_trait_refs.all(|(_, matches)| !matches));
23192334

2320-
VtableObjectData { object_ty: self_ty,
2321-
upcast_trait_ref: upcast_trait_ref }
2335+
VtableObjectData {
2336+
upcast_trait_ref: upcast_trait_ref.unwrap(),
2337+
vtable_base: vtable_base
2338+
}
23222339
}
23232340

23242341
fn confirm_fn_pointer_candidate(&mut self,
@@ -2719,7 +2736,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
27192736

27202737
/// Returns `Ok` if `poly_trait_ref` being true implies that the
27212738
/// obligation is satisfied.
2722-
fn match_poly_trait_ref(&mut self,
2739+
fn match_poly_trait_ref(&self,
27232740
obligation: &TraitObligation<'tcx>,
27242741
poly_trait_ref: ty::PolyTraitRef<'tcx>)
27252742
-> Result<(),()>
@@ -2930,32 +2947,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
29302947
obligation.cause.clone()
29312948
}
29322949
}
2933-
2934-
/// Upcasts an object trait-reference into those that match the obligation.
2935-
fn upcast(&mut self, obj_trait_ref: ty::PolyTraitRef<'tcx>, obligation: &TraitObligation<'tcx>)
2936-
-> Vec<ty::PolyTraitRef<'tcx>>
2937-
{
2938-
debug!("upcast(obj_trait_ref={:?}, obligation={:?})",
2939-
obj_trait_ref,
2940-
obligation);
2941-
2942-
let obligation_def_id = obligation.predicate.def_id();
2943-
let mut upcast_trait_refs = util::upcast(self.tcx(), obj_trait_ref, obligation_def_id);
2944-
2945-
// Retain only those upcast versions that match the trait-ref
2946-
// we are looking for. In particular, we know that all of
2947-
// `upcast_trait_refs` apply to the correct trait, but
2948-
// possibly with incorrect type parameters. For example, we
2949-
// may be trying to upcast `Foo` to `Bar<i32>`, but `Foo` is
2950-
// declared as `trait Foo : Bar<u32>`.
2951-
upcast_trait_refs.retain(|upcast_trait_ref| {
2952-
let upcast_trait_ref = upcast_trait_ref.clone();
2953-
self.infcx.probe(|_| self.match_poly_trait_ref(obligation, upcast_trait_ref)).is_ok()
2954-
});
2955-
2956-
debug!("upcast: upcast_trait_refs={:?}", upcast_trait_refs);
2957-
upcast_trait_refs
2958-
}
29592950
}
29602951

29612952
impl<'tcx> SelectionCache<'tcx> {

src/librustc/middle/traits/util.rs

+32-35
Original file line numberDiff line numberDiff line change
@@ -396,50 +396,45 @@ pub fn upcast<'tcx>(tcx: &ty::ctxt<'tcx>,
396396
.collect()
397397
}
398398

399-
/// Given an object of type `object_trait_ref`, returns the index of
400-
/// the method `method_def_id` (which should be part of a supertrait
401-
/// of `object_trait_ref`) within the vtable for `object_trait_ref`.
402-
pub fn get_vtable_index_of_object_method<'tcx>(tcx: &ty::ctxt<'tcx>,
403-
object_trait_ref: ty::PolyTraitRef<'tcx>,
404-
method_def_id: ast::DefId) -> usize {
405-
// We need to figure the "real index" of the method in a
406-
// listing of all the methods of an object. We do this by
407-
// iterating down the supertraits of the object's trait until
408-
// we find the trait the method came from, counting up the
409-
// methods from them.
410-
let mut method_count = 0;
411-
412-
let trait_def_id = tcx.impl_or_trait_item(method_def_id).container().id();
413-
414-
for bound_ref in transitive_bounds(tcx, &[object_trait_ref]) {
415-
if bound_ref.def_id() == trait_def_id {
416-
break;
417-
}
418-
419-
let trait_items = tcx.trait_items(bound_ref.def_id());
420-
for trait_item in trait_items.iter() {
421-
match *trait_item {
422-
ty::MethodTraitItem(_) => method_count += 1,
423-
_ => {}
424-
}
399+
/// Given an trait `trait_ref`, returns the number of vtable entries
400+
/// that come from `trait_ref`, excluding its supertraits. Used in
401+
/// computing the vtable base for an upcast trait of a trait object.
402+
pub fn count_own_vtable_entries<'tcx>(tcx: &ty::ctxt<'tcx>,
403+
trait_ref: ty::PolyTraitRef<'tcx>)
404+
-> usize {
405+
let mut entries = 0;
406+
// Count number of methods and add them to the total offset.
407+
// Skip over associated types and constants.
408+
for trait_item in &tcx.trait_items(trait_ref.def_id())[..] {
409+
if let ty::MethodTraitItem(_) = *trait_item {
410+
entries += 1;
425411
}
426412
}
413+
entries
414+
}
427415

428-
// count number of methods preceding the one we are selecting and
429-
// add them to the total offset; skip over associated types.
430-
for trait_item in &tcx.trait_items(trait_def_id)[..] {
416+
/// Given an upcast trait object described by `object`, returns the
417+
/// index of the method `method_def_id` (which should be part of
418+
/// `object.upcast_trait_ref`) within the vtable for `object`.
419+
pub fn get_vtable_index_of_object_method<'tcx>(tcx: &ty::ctxt<'tcx>,
420+
object: &super::VtableObjectData<'tcx>,
421+
method_def_id: ast::DefId) -> usize {
422+
// Count number of methods preceding the one we are selecting and
423+
// add them to the total offset.
424+
// Skip over associated types and constants.
425+
let mut entries = object.vtable_base;
426+
for trait_item in &tcx.trait_items(object.upcast_trait_ref.def_id())[..] {
431427
if trait_item.def_id() == method_def_id {
432428
// The item with the ID we were given really ought to be a method.
433429
assert!(match *trait_item {
434430
ty::MethodTraitItem(_) => true,
435431
_ => false
436432
});
437433

438-
return method_count;
434+
return entries;
439435
}
440-
match *trait_item {
441-
ty::MethodTraitItem(_) => method_count += 1,
442-
_ => {}
436+
if let ty::MethodTraitItem(_) = *trait_item {
437+
entries += 1;
443438
}
444439
}
445440

@@ -493,7 +488,7 @@ impl<'tcx, N:fmt::Debug> fmt::Debug for super::Vtable<'tcx, N> {
493488
write!(f, "VtableFnPointer({:?})", d),
494489

495490
super::VtableObject(ref d) =>
496-
write!(f, "VtableObject({:?})", d),
491+
write!(f, "{:?}", d),
497492

498493
super::VtableParam(ref n) =>
499494
write!(f, "VtableParam({:?})", n),
@@ -538,7 +533,9 @@ impl<'tcx, N:fmt::Debug> fmt::Debug for super::VtableDefaultImplData<N> {
538533

539534
impl<'tcx> fmt::Debug for super::VtableObjectData<'tcx> {
540535
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
541-
write!(f, "VtableObject(object_ty={:?})", self.object_ty)
536+
write!(f, "VtableObject(upcast={:?}, vtable_base={})",
537+
self.upcast_trait_ref,
538+
self.vtable_base)
542539
}
543540
}
544541

src/librustc/middle/ty_fold.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,8 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
492492
impl<'tcx> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx> {
493493
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableObjectData<'tcx> {
494494
traits::VtableObjectData {
495-
object_ty: self.object_ty.fold_with(folder),
496495
upcast_trait_ref: self.upcast_trait_ref.fold_with(folder),
496+
vtable_base: self.vtable_base
497497
}
498498
}
499499
}

src/librustc_trans/trans/meth.rs

+2-16
Original file line numberDiff line numberDiff line change
@@ -160,20 +160,6 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
160160
}
161161
}
162162

163-
fn method_offset_in_object_vtable<'tcx>(tcx: &ty::ctxt<'tcx>,
164-
object_ty: Ty<'tcx>,
165-
method_id: ast::DefId)
166-
-> usize {
167-
if let ty::TyTrait(ref data) = object_ty.sty {
168-
let trait_ref = data.principal_trait_ref_with_self_ty(tcx, object_ty);
169-
traits::get_vtable_index_of_object_method(tcx, trait_ref, method_id)
170-
} else {
171-
tcx.sess.bug(&format!(
172-
"trans::methd::object_ty_to_trait_ref() called on non-object: {:?}",
173-
object_ty));
174-
}
175-
}
176-
177163
pub fn trans_static_method_callee<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
178164
method_id: ast::DefId,
179165
trait_id: ast::DefId,
@@ -285,7 +271,7 @@ pub fn trans_static_method_callee<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
285271
callee_substs)
286272
}
287273
traits::VtableObject(ref data) => {
288-
let idx = method_offset_in_object_vtable(tcx, data.object_ty, method_id);
274+
let idx = traits::get_vtable_index_of_object_method(tcx, data, method_id);
289275
trans_object_shim(ccx,
290276
data.upcast_trait_ref.clone(),
291277
method_id,
@@ -384,7 +370,7 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
384370
}
385371
}
386372
traits::VtableObject(ref data) => {
387-
let idx = method_offset_in_object_vtable(bcx.tcx(), data.object_ty, method_id);
373+
let idx = traits::get_vtable_index_of_object_method(bcx.tcx(), data, method_id);
388374
if let Some(self_expr) = self_expr {
389375
if let ty::TyBareFn(_, ref fty) = monomorphize_type(bcx, method_ty).sty {
390376
let ty = bcx.tcx().mk_fn(None, opaque_method_ty(bcx.tcx(), fty));
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that the right implementation is called through a trait
12+
// object when supertraits include multiple references to the
13+
// same trait, with different type parameters.
14+
15+
trait A: PartialEq<Foo> + PartialEq<Bar> { }
16+
17+
struct Foo;
18+
struct Bar;
19+
20+
struct Aimpl;
21+
22+
impl PartialEq<Foo> for Aimpl {
23+
fn eq(&self, _rhs: &Foo) -> bool {
24+
true
25+
}
26+
}
27+
28+
impl PartialEq<Bar> for Aimpl {
29+
fn eq(&self, _rhs: &Bar) -> bool {
30+
false
31+
}
32+
}
33+
34+
impl A for Aimpl { }
35+
36+
fn main() {
37+
let a = &Aimpl as &A;
38+
39+
assert!(*a == Foo);
40+
}

0 commit comments

Comments
 (0)