Skip to content

Commit cd50b4e

Browse files
committed
Generalize the code so we can handle multiple supertraits.
Fixes #10596. Fixes #22279.
1 parent bc9ae36 commit cd50b4e

File tree

10 files changed

+178
-104
lines changed

10 files changed

+178
-104
lines changed

Diff for: src/librustc/middle/traits/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,11 @@ pub struct VtableBuiltinData<N> {
280280
/// for the object type `Foo`.
281281
#[derive(PartialEq,Eq,Clone)]
282282
pub struct VtableObjectData<'tcx> {
283+
/// the object type `Foo`.
283284
pub object_ty: Ty<'tcx>,
285+
286+
/// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`.
287+
pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,
284288
}
285289

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

Diff for: src/librustc/middle/traits/select.rs

+36-27
Original file line numberDiff line numberDiff line change
@@ -1260,19 +1260,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12601260
poly_trait_ref.repr(self.tcx()));
12611261

12621262
// see whether the object trait can be upcast to the trait we are looking for
1263-
let obligation_def_id = obligation.predicate.def_id();
1264-
let upcast_trait_ref = match util::upcast(self.tcx(), poly_trait_ref, obligation_def_id) {
1265-
Some(r) => r,
1266-
None => { return; }
1267-
};
1268-
1269-
debug!("assemble_candidates_from_object_ty: upcast_trait_ref={}",
1270-
upcast_trait_ref.repr(self.tcx()));
1271-
1272-
// check whether the upcast version of the trait-ref matches what we are looking for
1273-
if let Ok(()) = self.infcx.probe(|_| self.match_poly_trait_ref(obligation,
1274-
upcast_trait_ref.clone())) {
1275-
debug!("assemble_candidates_from_object_ty: matched, pushing candidate");
1263+
let upcast_trait_refs = self.upcast(poly_trait_ref, obligation);
1264+
if upcast_trait_refs.len() > 1 {
1265+
// can be upcast in many ways; need more type information
1266+
candidates.ambiguous = true;
1267+
} else if upcast_trait_refs.len() == 1 {
12761268
candidates.vec.push(ObjectCandidate);
12771269
}
12781270
}
@@ -2063,28 +2055,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
20632055
}
20642056
};
20652057

2066-
let obligation_def_id = obligation.predicate.def_id();
2067-
let upcast_trait_ref = match util::upcast(self.tcx(),
2068-
poly_trait_ref.clone(),
2069-
obligation_def_id) {
2070-
Some(r) => r,
2071-
None => {
2072-
self.tcx().sess.span_bug(obligation.cause.span,
2073-
&format!("unable to upcast from {} to {}",
2074-
poly_trait_ref.repr(self.tcx()),
2075-
obligation_def_id.repr(self.tcx())));
2076-
}
2077-
};
2058+
// Upcast the object type to the obligation type. There must
2059+
// be exactly one applicable trait-reference; if this were not
2060+
// the case, we would have reported an ambiguity error rather
2061+
// than successfully selecting one of the candidates.
2062+
let upcast_trait_refs = self.upcast(poly_trait_ref.clone(), obligation);
2063+
assert_eq!(upcast_trait_refs.len(), 1);
2064+
let upcast_trait_ref = upcast_trait_refs.into_iter().next().unwrap();
20782065

2079-
match self.match_poly_trait_ref(obligation, upcast_trait_ref) {
2066+
match self.match_poly_trait_ref(obligation, upcast_trait_ref.clone()) {
20802067
Ok(()) => { }
20812068
Err(()) => {
20822069
self.tcx().sess.span_bug(obligation.cause.span,
20832070
"failed to match trait refs");
20842071
}
20852072
}
20862073

2087-
VtableObjectData { object_ty: self_ty }
2074+
VtableObjectData { object_ty: self_ty,
2075+
upcast_trait_ref: upcast_trait_ref }
20882076
}
20892077

20902078
fn confirm_fn_pointer_candidate(&mut self,
@@ -2501,6 +2489,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
25012489
obligation.cause.clone()
25022490
}
25032491
}
2492+
2493+
/// Upcasts an object trait-reference into those that match the obligation.
2494+
fn upcast(&mut self, obj_trait_ref: ty::PolyTraitRef<'tcx>, obligation: &TraitObligation<'tcx>)
2495+
-> Vec<ty::PolyTraitRef<'tcx>>
2496+
{
2497+
debug!("upcast(obj_trait_ref={}, obligation={})",
2498+
obj_trait_ref.repr(self.tcx()),
2499+
obligation.repr(self.tcx()));
2500+
2501+
let obligation_def_id = obligation.predicate.def_id();
2502+
let mut upcast_trait_refs = util::upcast(self.tcx(), obj_trait_ref, obligation_def_id);
2503+
2504+
// retain only those upcast versions that match the trait-ref we are looking for
2505+
upcast_trait_refs.retain(|upcast_trait_ref| {
2506+
let upcast_trait_ref = upcast_trait_ref.clone();
2507+
self.infcx.probe(|_| self.match_poly_trait_ref(obligation, upcast_trait_ref)).is_ok()
2508+
});
2509+
2510+
debug!("upcast: upcast_trait_refs={}", upcast_trait_refs.repr(self.tcx()));
2511+
upcast_trait_refs
2512+
}
25042513
}
25052514

25062515
impl<'tcx> Repr<'tcx> for SelectionCandidate<'tcx> {

Diff for: src/librustc/middle/traits/util.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -359,19 +359,15 @@ pub fn predicate_for_builtin_bound<'tcx>(
359359
pub fn upcast<'tcx>(tcx: &ty::ctxt<'tcx>,
360360
source_trait_ref: ty::PolyTraitRef<'tcx>,
361361
target_trait_def_id: ast::DefId)
362-
-> Option<ty::PolyTraitRef<'tcx>>
362+
-> Vec<ty::PolyTraitRef<'tcx>>
363363
{
364364
if source_trait_ref.def_id() == target_trait_def_id {
365-
return Some(source_trait_ref); // shorcut the most common case
365+
return vec![source_trait_ref]; // shorcut the most common case
366366
}
367367

368-
for super_trait_ref in supertraits(tcx, source_trait_ref) {
369-
if super_trait_ref.def_id() == target_trait_def_id {
370-
return Some(super_trait_ref);
371-
}
372-
}
373-
374-
None
368+
supertraits(tcx, source_trait_ref)
369+
.filter(|r| r.def_id() == target_trait_def_id)
370+
.collect()
375371
}
376372

377373
/// Given an object of type `object_trait_ref`, returns the index of

Diff for: src/librustc/middle/ty_fold.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,8 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
544544
impl<'tcx> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx> {
545545
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> traits::VtableObjectData<'tcx> {
546546
traits::VtableObjectData {
547-
object_ty: self.object_ty.fold_with(folder)
547+
object_ty: self.object_ty.fold_with(folder),
548+
upcast_trait_ref: self.upcast_trait_ref.fold_with(folder),
548549
}
549550
}
550551
}

Diff for: src/librustc_trans/trans/meth.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,8 @@ pub fn trans_static_method_callee<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
300300
.position(|item| item.def_id() == method_id)
301301
.unwrap();
302302
let (llfn, ty) =
303-
trans_object_shim(ccx, data.object_ty, trait_id, method_offset_in_trait);
303+
trans_object_shim(ccx, data.object_ty, data.upcast_trait_ref.clone(),
304+
method_offset_in_trait);
304305
immediate_rvalue(llfn, ty)
305306
}
306307
_ => {
@@ -386,7 +387,8 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
386387
Callee { bcx: bcx, data: Fn(llfn) }
387388
}
388389
traits::VtableObject(ref data) => {
389-
let (llfn, _) = trans_object_shim(bcx.ccx(), data.object_ty, trait_id, n_method);
390+
let (llfn, _) = trans_object_shim(bcx.ccx(), data.object_ty,
391+
data.upcast_trait_ref.clone(), n_method);
390392
Callee { bcx: bcx, data: Fn(llfn) }
391393
}
392394
traits::VtableBuiltin(..) |
@@ -551,16 +553,17 @@ pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
551553
pub fn trans_object_shim<'a, 'tcx>(
552554
ccx: &'a CrateContext<'a, 'tcx>,
553555
object_ty: Ty<'tcx>,
554-
trait_id: ast::DefId,
556+
upcast_trait_ref: ty::PolyTraitRef<'tcx>,
555557
method_offset_in_trait: uint)
556558
-> (ValueRef, Ty<'tcx>)
557559
{
558560
let _icx = push_ctxt("trans_object_shim");
559561
let tcx = ccx.tcx();
562+
let trait_id = upcast_trait_ref.def_id();
560563

561-
debug!("trans_object_shim(object_ty={}, trait_id={}, method_offset_in_trait={})",
564+
debug!("trans_object_shim(object_ty={}, upcast_trait_ref={}, method_offset_in_trait={})",
562565
object_ty.repr(tcx),
563-
trait_id.repr(tcx),
566+
upcast_trait_ref.repr(tcx),
564567
method_offset_in_trait);
565568

566569
let object_trait_ref =
@@ -575,7 +578,6 @@ pub fn trans_object_shim<'a, 'tcx>(
575578
};
576579

577580
// Upcast to the trait in question and extract out the substitutions.
578-
let upcast_trait_ref = traits::upcast(ccx.tcx(), object_trait_ref.clone(), trait_id).unwrap();
579581
let upcast_trait_ref = ty::erase_late_bound_regions(tcx, &upcast_trait_ref);
580582
let object_substs = upcast_trait_ref.substs.clone().erase_regions();
581583
debug!("trans_object_shim: object_substs={}", object_substs.repr(tcx));

Diff for: src/librustc_typeck/check/method/confirm.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -634,16 +634,21 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
634634
target_trait_def_id: ast::DefId)
635635
-> ty::PolyTraitRef<'tcx>
636636
{
637-
match traits::upcast(self.tcx(), source_trait_ref.clone(), target_trait_def_id) {
638-
Some(super_trait_ref) => super_trait_ref,
639-
None => {
640-
self.tcx().sess.span_bug(
641-
self.span,
642-
&format!("cannot upcast `{}` to `{}`",
643-
source_trait_ref.repr(self.tcx()),
644-
target_trait_def_id.repr(self.tcx())));
645-
}
637+
let upcast_trait_refs = traits::upcast(self.tcx(),
638+
source_trait_ref.clone(),
639+
target_trait_def_id);
640+
641+
// must be exactly one trait ref or we'd get an ambig error etc
642+
if upcast_trait_refs.len() != 1 {
643+
self.tcx().sess.span_bug(
644+
self.span,
645+
&format!("cannot uniquely upcast `{}` to `{}`: `{}`",
646+
source_trait_ref.repr(self.tcx()),
647+
target_trait_def_id.repr(self.tcx()),
648+
upcast_trait_refs.repr(self.tcx())));
646649
}
650+
651+
upcast_trait_refs.into_iter().next().unwrap()
647652
}
648653

649654
fn replace_late_bound_regions_with_fresh_var<T>(&self, value: &ty::Binder<T>) -> T

Diff for: src/test/compile-fail/issue-3953.rs

-33
This file was deleted.
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 a case of a trait which extends the same supertrait twice, but
12+
// with difference type parameters. Test then that when we don't give
13+
// enough information to pick between these, no selection is made. In
14+
// this particular case, the two choices are i64/u64 -- so when we use
15+
// an integer literal, we wind up falling this literal back to i32.
16+
// See also `run-pass/trait-repeated-supertrait.rs`.
17+
18+
trait CompareTo<T> {
19+
fn same_as(&self, t: T) -> bool;
20+
}
21+
22+
trait CompareToInts : CompareTo<i64> + CompareTo<u64> {
23+
}
24+
25+
impl CompareTo<i64> for i64 {
26+
fn same_as(&self, t: i64) -> bool { *self == t }
27+
}
28+
29+
impl CompareTo<u64> for i64 {
30+
fn same_as(&self, t: u64) -> bool { *self == (t as i64) }
31+
}
32+
33+
impl CompareToInts for i64 { }
34+
35+
fn with_obj(c: &CompareToInts) -> bool {
36+
c.same_as(22) //~ ERROR `CompareTo<i32>` is not implemented
37+
}
38+
39+
fn with_trait<C:CompareToInts>(c: &C) -> bool {
40+
c.same_as(22) //~ ERROR `CompareTo<i32>` is not implemented
41+
}
42+
43+
fn with_ufcs1<C:CompareToInts>(c: &C) -> bool {
44+
CompareToInts::same_as(c, 22) //~ ERROR `CompareTo<i32>` is not implemented
45+
}
46+
47+
fn with_ufcs2<C:CompareToInts>(c: &C) -> bool {
48+
CompareTo::same_as(c, 22) //~ ERROR `CompareTo<i32>` is not implemented
49+
}
50+
51+
fn main() {
52+
assert_eq!(22_i64.same_as(22), true); //~ ERROR `CompareTo<i32>` is not implemented
53+
}

Diff for: src/test/compile-fail/unsized4.rs

-19
This file was deleted.

Diff for: src/test/run-pass/traits-repeated-supertrait.rs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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 a case of a trait which extends the same supertrait twice, but
12+
// with difference type parameters. Test that we can invoke the
13+
// various methods in various ways successfully.
14+
// See also `compile-fail/trait-repeated-supertrait-ambig.rs`.
15+
16+
trait CompareTo<T> {
17+
fn same_as(&self, t: T) -> bool;
18+
}
19+
20+
trait CompareToInts : CompareTo<i64> + CompareTo<u64> {
21+
}
22+
23+
impl CompareTo<i64> for i64 {
24+
fn same_as(&self, t: i64) -> bool { *self == t }
25+
}
26+
27+
impl CompareTo<u64> for i64 {
28+
fn same_as(&self, t: u64) -> bool { *self == (t as i64) }
29+
}
30+
31+
impl CompareToInts for i64 { }
32+
33+
fn with_obj(c: &CompareToInts) -> bool {
34+
c.same_as(22_i64) && c.same_as(22_u64)
35+
}
36+
37+
fn with_trait<C:CompareToInts>(c: &C) -> bool {
38+
c.same_as(22_i64) && c.same_as(22_u64)
39+
}
40+
41+
fn with_ufcs1<C:CompareToInts>(c: &C) -> bool {
42+
CompareToInts::same_as(c, 22_i64) && CompareToInts::same_as(c, 22_u64)
43+
}
44+
45+
fn with_ufcs2<C:CompareToInts>(c: &C) -> bool {
46+
CompareTo::same_as(c, 22_i64) && CompareTo::same_as(c, 22_u64)
47+
}
48+
49+
fn main() {
50+
assert_eq!(22_i64.same_as(22_i64), true);
51+
assert_eq!(22_i64.same_as(22_u64), true);
52+
assert_eq!(with_trait(&22), true);
53+
assert_eq!(with_obj(&22), true);
54+
assert_eq!(with_ufcs1(&22), true);
55+
assert_eq!(with_ufcs2(&22), true);
56+
}

0 commit comments

Comments
 (0)