1
Fork 0

Generalize the code so we can handle multiple supertraits.

Fixes #10596. Fixes #22279.
This commit is contained in:
Niko Matsakis 2015-03-03 08:01:13 -05:00
parent bc9ae36dba
commit cd50b4e0b1
10 changed files with 178 additions and 104 deletions

View file

@ -280,7 +280,11 @@ 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>,
}
/// Creates predicate obligations from the generic bounds.

View file

@ -1260,19 +1260,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
poly_trait_ref.repr(self.tcx()));
// see whether the object trait can be upcast to the trait we are looking for
let obligation_def_id = obligation.predicate.def_id();
let upcast_trait_ref = match util::upcast(self.tcx(), poly_trait_ref, obligation_def_id) {
Some(r) => r,
None => { return; }
};
debug!("assemble_candidates_from_object_ty: upcast_trait_ref={}",
upcast_trait_ref.repr(self.tcx()));
// check whether the upcast version of the trait-ref matches what we are looking for
if let Ok(()) = self.infcx.probe(|_| self.match_poly_trait_ref(obligation,
upcast_trait_ref.clone())) {
debug!("assemble_candidates_from_object_ty: matched, pushing candidate");
let upcast_trait_refs = self.upcast(poly_trait_ref, obligation);
if upcast_trait_refs.len() > 1 {
// can be upcast in many ways; need more type information
candidates.ambiguous = true;
} else if upcast_trait_refs.len() == 1 {
candidates.vec.push(ObjectCandidate);
}
}
@ -2063,20 +2055,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
};
let obligation_def_id = obligation.predicate.def_id();
let upcast_trait_ref = match util::upcast(self.tcx(),
poly_trait_ref.clone(),
obligation_def_id) {
Some(r) => r,
None => {
self.tcx().sess.span_bug(obligation.cause.span,
&format!("unable to upcast from {} to {}",
poly_trait_ref.repr(self.tcx()),
obligation_def_id.repr(self.tcx())));
}
};
// Upcast the object type to the obligation type. There must
// 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();
match self.match_poly_trait_ref(obligation, upcast_trait_ref) {
match self.match_poly_trait_ref(obligation, upcast_trait_ref.clone()) {
Ok(()) => { }
Err(()) => {
self.tcx().sess.span_bug(obligation.cause.span,
@ -2084,7 +2071,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
VtableObjectData { object_ty: self_ty }
VtableObjectData { object_ty: self_ty,
upcast_trait_ref: upcast_trait_ref }
}
fn confirm_fn_pointer_candidate(&mut self,
@ -2501,6 +2489,27 @@ 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.repr(self.tcx()),
obligation.repr(self.tcx()));
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
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.repr(self.tcx()));
upcast_trait_refs
}
}
impl<'tcx> Repr<'tcx> for SelectionCandidate<'tcx> {

View file

@ -359,19 +359,15 @@ pub fn predicate_for_builtin_bound<'tcx>(
pub fn upcast<'tcx>(tcx: &ty::ctxt<'tcx>,
source_trait_ref: ty::PolyTraitRef<'tcx>,
target_trait_def_id: ast::DefId)
-> Option<ty::PolyTraitRef<'tcx>>
-> Vec<ty::PolyTraitRef<'tcx>>
{
if source_trait_ref.def_id() == target_trait_def_id {
return Some(source_trait_ref); // shorcut the most common case
return vec![source_trait_ref]; // shorcut the most common case
}
for super_trait_ref in supertraits(tcx, source_trait_ref) {
if super_trait_ref.def_id() == target_trait_def_id {
return Some(super_trait_ref);
}
}
None
supertraits(tcx, source_trait_ref)
.filter(|r| r.def_id() == target_trait_def_id)
.collect()
}
/// Given an object of type `object_trait_ref`, returns the index of

View file

@ -544,7 +544,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)
object_ty: self.object_ty.fold_with(folder),
upcast_trait_ref: self.upcast_trait_ref.fold_with(folder),
}
}
}

View file

@ -300,7 +300,8 @@ pub fn trans_static_method_callee<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
.position(|item| item.def_id() == method_id)
.unwrap();
let (llfn, ty) =
trans_object_shim(ccx, data.object_ty, trait_id, method_offset_in_trait);
trans_object_shim(ccx, data.object_ty, data.upcast_trait_ref.clone(),
method_offset_in_trait);
immediate_rvalue(llfn, ty)
}
_ => {
@ -386,7 +387,8 @@ fn trans_monomorphized_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
Callee { bcx: bcx, data: Fn(llfn) }
}
traits::VtableObject(ref data) => {
let (llfn, _) = trans_object_shim(bcx.ccx(), data.object_ty, trait_id, n_method);
let (llfn, _) = trans_object_shim(bcx.ccx(), data.object_ty,
data.upcast_trait_ref.clone(), n_method);
Callee { bcx: bcx, data: Fn(llfn) }
}
traits::VtableBuiltin(..) |
@ -551,16 +553,17 @@ pub fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
pub fn trans_object_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
object_ty: Ty<'tcx>,
trait_id: ast::DefId,
upcast_trait_ref: ty::PolyTraitRef<'tcx>,
method_offset_in_trait: uint)
-> (ValueRef, Ty<'tcx>)
{
let _icx = push_ctxt("trans_object_shim");
let tcx = ccx.tcx();
let trait_id = upcast_trait_ref.def_id();
debug!("trans_object_shim(object_ty={}, trait_id={}, method_offset_in_trait={})",
debug!("trans_object_shim(object_ty={}, upcast_trait_ref={}, method_offset_in_trait={})",
object_ty.repr(tcx),
trait_id.repr(tcx),
upcast_trait_ref.repr(tcx),
method_offset_in_trait);
let object_trait_ref =
@ -575,7 +578,6 @@ pub fn trans_object_shim<'a, 'tcx>(
};
// Upcast to the trait in question and extract out the substitutions.
let upcast_trait_ref = traits::upcast(ccx.tcx(), object_trait_ref.clone(), trait_id).unwrap();
let upcast_trait_ref = ty::erase_late_bound_regions(tcx, &upcast_trait_ref);
let object_substs = upcast_trait_ref.substs.clone().erase_regions();
debug!("trans_object_shim: object_substs={}", object_substs.repr(tcx));

View file

@ -634,16 +634,21 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
target_trait_def_id: ast::DefId)
-> ty::PolyTraitRef<'tcx>
{
match traits::upcast(self.tcx(), source_trait_ref.clone(), target_trait_def_id) {
Some(super_trait_ref) => super_trait_ref,
None => {
self.tcx().sess.span_bug(
self.span,
&format!("cannot upcast `{}` to `{}`",
source_trait_ref.repr(self.tcx()),
target_trait_def_id.repr(self.tcx())));
}
let upcast_trait_refs = traits::upcast(self.tcx(),
source_trait_ref.clone(),
target_trait_def_id);
// must be exactly one trait ref or we'd get an ambig error etc
if upcast_trait_refs.len() != 1 {
self.tcx().sess.span_bug(
self.span,
&format!("cannot uniquely upcast `{}` to `{}`: `{}`",
source_trait_ref.repr(self.tcx()),
target_trait_def_id.repr(self.tcx()),
upcast_trait_refs.repr(self.tcx())));
}
upcast_trait_refs.into_iter().next().unwrap()
}
fn replace_late_bound_regions_with_fresh_var<T>(&self, value: &ty::Binder<T>) -> T

View file

@ -1,33 +0,0 @@
// Copyright 2012 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.
// ignore-tidy-linelength
use std::cmp::PartialEq;
trait Hahaha: PartialEq + PartialEq {
}
struct Lol(isize);
impl Hahaha for Lol { }
impl PartialEq for Lol {
fn eq(&self, other: &Lol) -> bool { loop { } }
fn ne(&self, other: &Lol) -> bool { loop { } }
}
fn main() {
if Lol(2) == Lol(4) {
println!("2 == 4");
} else {
println!("2 != 4");
}
}

View file

@ -0,0 +1,53 @@
// 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 a case of a trait which extends the same supertrait twice, but
// with difference type parameters. Test then that when we don't give
// enough information to pick between these, no selection is made. In
// this particular case, the two choices are i64/u64 -- so when we use
// an integer literal, we wind up falling this literal back to i32.
// See also `run-pass/trait-repeated-supertrait.rs`.
trait CompareTo<T> {
fn same_as(&self, t: T) -> bool;
}
trait CompareToInts : CompareTo<i64> + CompareTo<u64> {
}
impl CompareTo<i64> for i64 {
fn same_as(&self, t: i64) -> bool { *self == t }
}
impl CompareTo<u64> for i64 {
fn same_as(&self, t: u64) -> bool { *self == (t as i64) }
}
impl CompareToInts for i64 { }
fn with_obj(c: &CompareToInts) -> bool {
c.same_as(22) //~ ERROR `CompareTo<i32>` is not implemented
}
fn with_trait<C:CompareToInts>(c: &C) -> bool {
c.same_as(22) //~ ERROR `CompareTo<i32>` is not implemented
}
fn with_ufcs1<C:CompareToInts>(c: &C) -> bool {
CompareToInts::same_as(c, 22) //~ ERROR `CompareTo<i32>` is not implemented
}
fn with_ufcs2<C:CompareToInts>(c: &C) -> bool {
CompareTo::same_as(c, 22) //~ ERROR `CompareTo<i32>` is not implemented
}
fn main() {
assert_eq!(22_i64.same_as(22), true); //~ ERROR `CompareTo<i32>` is not implemented
}

View file

@ -1,19 +0,0 @@
// Copyright 2014 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 bounds are sized-compatible.
trait T : Sized {}
fn f<Y: ?Sized + T>() {
//~^ERROR incompatible bounds on `Y`, bound `T` does not allow unsized type
}
pub fn main() {
}

View file

@ -0,0 +1,56 @@
// 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 a case of a trait which extends the same supertrait twice, but
// with difference type parameters. Test that we can invoke the
// various methods in various ways successfully.
// See also `compile-fail/trait-repeated-supertrait-ambig.rs`.
trait CompareTo<T> {
fn same_as(&self, t: T) -> bool;
}
trait CompareToInts : CompareTo<i64> + CompareTo<u64> {
}
impl CompareTo<i64> for i64 {
fn same_as(&self, t: i64) -> bool { *self == t }
}
impl CompareTo<u64> for i64 {
fn same_as(&self, t: u64) -> bool { *self == (t as i64) }
}
impl CompareToInts for i64 { }
fn with_obj(c: &CompareToInts) -> bool {
c.same_as(22_i64) && c.same_as(22_u64)
}
fn with_trait<C:CompareToInts>(c: &C) -> bool {
c.same_as(22_i64) && c.same_as(22_u64)
}
fn with_ufcs1<C:CompareToInts>(c: &C) -> bool {
CompareToInts::same_as(c, 22_i64) && CompareToInts::same_as(c, 22_u64)
}
fn with_ufcs2<C:CompareToInts>(c: &C) -> bool {
CompareTo::same_as(c, 22_i64) && CompareTo::same_as(c, 22_u64)
}
fn main() {
assert_eq!(22_i64.same_as(22_i64), true);
assert_eq!(22_i64.same_as(22_u64), true);
assert_eq!(with_trait(&22), true);
assert_eq!(with_obj(&22), true);
assert_eq!(with_ufcs1(&22), true);
assert_eq!(with_ufcs2(&22), true);
}