Implement trait inheritance for bounded type parameters

This commit is contained in:
Brian Anderson 2012-11-28 12:34:30 -08:00
parent daa89e0861
commit 78ee821154
38 changed files with 1004 additions and 176 deletions

View file

@ -1330,16 +1330,12 @@ fn find_vtable(tcx: ty::ctxt, ps: &param_substs,
debug!("find_vtable_in_fn_ctxt(n_param=%u, n_bound=%u, ps=%?)",
n_param, n_bound, param_substs_to_str(tcx, ps));
let mut vtable_off = n_bound, i = 0u;
// Vtables are stored in a flat array, finding the right one is
// somewhat awkward
for vec::each(*ps.bounds) |bounds| {
if i >= n_param { break; }
for vec::each(**bounds) |bound| {
match *bound { ty::bound_trait(_) => vtable_off += 1u, _ => () }
}
i += 1u;
}
let first_n_bounds = ps.bounds.view(0, n_param);
let vtables_to_skip =
ty::count_traits_and_supertraits(tcx, first_n_bounds);
let vtable_off = vtables_to_skip + n_bound;
ps.vtables.get()[vtable_off]
}

View file

@ -243,17 +243,7 @@ fn trans_static_method_callee(bcx: block,
// one we are interested in.
let bound_index = {
let trait_polyty = ty::lookup_item_type(bcx.tcx(), trait_id);
let mut index = 0;
for trait_polyty.bounds.each |param_bounds| {
for param_bounds.each |param_bound| {
match *param_bound {
ty::bound_trait(_) => { index += 1; }
ty::bound_copy | ty::bound_owned |
ty::bound_send | ty::bound_const => {}
}
}
}
index
ty::count_traits_and_supertraits(bcx.tcx(), *trait_polyty.bounds)
};
let mname = if method_id.crate == ast::local_crate {

View file

@ -204,6 +204,8 @@ export DerivedFieldInfo;
export AutoAdjustment;
export AutoRef;
export AutoRefKind, AutoPtr, AutoBorrowVec, AutoBorrowFn;
export iter_bound_traits_and_supertraits;
export count_traits_and_supertraits;
// Data types
@ -4530,6 +4532,64 @@ pure fn determine_inherited_purity(parent_purity: ast::purity,
}
}
// Iterate over a type parameter's bounded traits and any supertraits
// of those traits, ignoring kinds.
fn iter_bound_traits_and_supertraits(tcx: ctxt,
bounds: param_bounds,
f: &fn(t) -> bool) {
for bounds.each |bound| {
let bound_trait_ty = match *bound {
ty::bound_trait(bound_t) => bound_t,
ty::bound_copy | ty::bound_send |
ty::bound_const | ty::bound_owned => {
loop; // skip non-trait bounds
}
};
let mut worklist = ~[];
let init_trait_ty = bound_trait_ty;
worklist.push(init_trait_ty);
let mut i = 0;
while i < worklist.len() {
let init_trait_ty = worklist[i];
i += 1;
let init_trait_id = match ty_to_def_id(init_trait_ty) {
Some(id) => id,
None => tcx.sess.bug(
~"trait type should have def_id")
};
// Add supertraits to worklist
let supertraits = trait_supertraits(tcx,
init_trait_id);
for supertraits.each |supertrait| {
worklist.push(supertrait.tpt.ty);
}
if !f(init_trait_ty) {
return;
}
}
}
}
fn count_traits_and_supertraits(tcx: ctxt,
boundses: &[param_bounds]) -> uint {
let mut total = 0;
for boundses.each |bounds| {
for iter_bound_traits_and_supertraits(tcx, *bounds) |_trait_ty| {
total += 1;
}
}
return total;
}
impl mt : cmp::Eq {
pure fn eq(&self, other: &mt) -> bool {
(*self).ty == (*other).ty && (*self).mutbl == (*other).mutbl

View file

@ -241,7 +241,7 @@ impl LookupContext {
loop {
match get(self_ty).sty {
ty_param(p) => {
self.push_inherent_candidates_from_param(p);
self.push_inherent_candidates_from_param(self_ty, p);
}
ty_trait(did, ref substs, vstore) => {
self.push_inherent_candidates_from_trait(
@ -305,7 +305,8 @@ impl LookupContext {
}
}
fn push_inherent_candidates_from_param(&self, param_ty: param_ty) {
fn push_inherent_candidates_from_param(&self, rcvr_ty: ty::t,
param_ty: param_ty) {
debug!("push_inherent_candidates_from_param(param_ty=%?)",
param_ty);
let _indenter = indenter();
@ -313,8 +314,9 @@ impl LookupContext {
let tcx = self.tcx();
let mut next_bound_idx = 0; // count only trait bounds
let bounds = tcx.ty_param_bounds.get(param_ty.def_id.node);
for vec::each(*bounds) |bound| {
let bound_t = match *bound {
let bound_trait_ty = match *bound {
ty::bound_trait(bound_t) => bound_t,
ty::bound_copy | ty::bound_send |
@ -323,56 +325,64 @@ impl LookupContext {
}
};
let this_bound_idx = next_bound_idx;
next_bound_idx += 1;
let (trait_id, bound_substs) = match ty::get(bound_t).sty {
ty::ty_trait(i, substs, _) => (i, substs),
let bound_substs = match ty::get(bound_trait_ty).sty {
ty::ty_trait(_, substs, _) => substs,
_ => {
self.bug(fmt!("add_candidates_from_param: \
non-trait bound %s",
self.ty_to_str(bound_t)));
self.ty_to_str(bound_trait_ty)));
}
};
// Loop over the trait and all of its supertraits.
let worklist = dvec::DVec();
worklist.push((trait_id, move bound_substs));
let mut worklist = ~[];
let init_trait_ty = bound_trait_ty;
let init_substs = bound_substs;
// Replace any appearance of `self` with the type of the
// generic parameter itself. Note that this is the only
// case where this replacement is necessary: in all other
// cases, we are either invoking a method directly from an
// impl or class (where the self type is not permitted),
// or from a trait type (in which case methods that refer
// to self are not permitted).
let init_substs = {self_ty: Some(rcvr_ty), ..init_substs};
worklist.push((init_trait_ty, init_substs));
let mut i = 0;
while i < worklist.len() {
let (trait_id, bound_substs) = worklist[i];
let (init_trait_ty, init_substs) = worklist[i];
i += 1;
// Replace any appearance of `self` with the type of the
// generic parameter itself. Note that this is the only
// case where this replacement is necessary: in all other
// cases, we are either invoking a method directly from an
// impl or class (where the self type is not permitted),
// or from a trait type (in which case methods that refer
// to self are not permitted).
let rcvr_ty = ty::mk_param(tcx, param_ty.idx,
param_ty.def_id);
let rcvr_substs = {self_ty: Some(rcvr_ty), ..bound_substs};
let init_trait_id = ty::ty_to_def_id(init_trait_ty).get();
// Add all the supertraits of this trait to the worklist.
debug!("finding supertraits for %d:%d", trait_id.crate,
trait_id.node);
let instantiated_trait_refs = ty::trait_supertraits(
tcx, trait_id);
for instantiated_trait_refs.each |instantiated_trait_ref| {
debug!("adding supertrait");
let supertraits = ty::trait_supertraits(tcx,
init_trait_id);
for supertraits.each |supertrait| {
debug!("adding supertrait: %?",
supertrait.def_id);
let new_substs = ty::subst_substs(
tcx,
&instantiated_trait_ref.tpt.substs,
&rcvr_substs);
&supertrait.tpt.substs,
&init_substs);
worklist.push(
(instantiated_trait_ref.def_id, new_substs));
// Again replacing the self type
let new_substs = {self_ty: Some(rcvr_ty), ..new_substs};
worklist.push((supertrait.tpt.ty, new_substs));
}
let trait_methods = ty::trait_methods(tcx, trait_id);
let this_bound_idx = next_bound_idx;
next_bound_idx += 1;
let trait_methods = ty::trait_methods(tcx, init_trait_id);
let pos = {
// FIXME #3453 can't use trait_methods.position
match vec::position(*trait_methods,
@ -381,6 +391,8 @@ impl LookupContext {
{
Some(pos) => pos,
None => {
debug!("trait doesn't contain method: %?",
init_trait_id);
loop; // check next trait or bound
}
}
@ -389,18 +401,21 @@ impl LookupContext {
let (rcvr_ty, rcvr_substs) =
self.create_rcvr_ty_and_substs_for_method(
method.self_ty, rcvr_ty, move rcvr_substs);
method.self_ty, rcvr_ty, move init_substs);
self.inherent_candidates.push(Candidate {
let cand = Candidate {
rcvr_ty: rcvr_ty,
rcvr_substs: rcvr_substs,
num_method_tps: method.tps.len(),
self_mode: get_mode_from_self_type(method.self_ty),
origin: method_param({trait_id:trait_id,
origin: method_param({trait_id:init_trait_id,
method_num:pos,
param_num:param_ty.idx,
bound_num:this_bound_idx})
});
};
debug!("pushing inherent candidate for param: %?", cand);
self.inherent_candidates.push(cand);
}
}
}
@ -775,6 +790,8 @@ impl LookupContext {
let relevant_candidates =
candidates.filter_to_vec(|c| self.is_relevant(self_ty, &c));
let relevant_candidates = self.merge_candidates(relevant_candidates);
if relevant_candidates.len() == 0 {
return None;
}
@ -791,6 +808,52 @@ impl LookupContext {
Some(self.confirm_candidate(self_ty, &relevant_candidates[0]))
}
fn merge_candidates(&self, candidates: &[Candidate]) -> ~[Candidate] {
let mut merged = ~[];
let mut i = 0;
while i < candidates.len() {
let candidate_a = candidates[i];
let mut skip = false;
let mut j = i + 1;
while j < candidates.len() {
let candidate_b = candidates[j];
debug!("attempting to merge %? and %?",
candidate_a, candidate_b);
let candidates_same = match (&candidate_a.origin,
&candidate_b.origin) {
(&method_param(p1), &method_param(p2)) => {
let same_trait = p1.trait_id == p2.trait_id;
let same_method = p1.method_num == p2.method_num;
let same_param = p1.param_num == p2.param_num;
// The bound number may be different because
// multiple bounds may lead to the same trait
// impl
same_trait && same_method && same_param
}
_ => false
};
if candidates_same {
skip = true;
break;
}
j += 1;
}
i += 1;
if skip {
// There are more than one of these and we need only one
loop;
} else {
merged.push(candidate_a);
}
}
return merged;
}
fn confirm_candidate(&self,
self_ty: ty::t,
candidate: &Candidate)

View file

@ -67,28 +67,43 @@ fn lookup_vtables(vcx: &VtableContext,
let tcx = vcx.tcx();
let mut result = ~[], i = 0u;
for substs.tps.each |ty| {
for vec::each(*bounds[i]) |bound| {
match *bound {
ty::bound_trait(i_ty) => {
let i_ty = ty::subst(tcx, substs, i_ty);
match lookup_vtable_covariant(vcx, location_info, *ty, i_ty,
allow_unsafe, is_early) {
Some(vtable) => result.push(vtable),
None => {
vcx.tcx().sess.span_fatal(
location_info.span,
fmt!("failed to find an implementation of trait \
%s for %s",
ty_to_str(vcx.tcx(), i_ty),
ty_to_str(vcx.tcx(), *ty)));
}
for ty::iter_bound_traits_and_supertraits(
tcx, bounds[i]) |trait_ty| {
debug!("about to subst: %?, %?",
ty_to_str(tcx, trait_ty),
ty::substs_to_str(tcx, substs));
let new_substs = {self_ty: Some(*ty), ..*substs};
let trait_ty = ty::subst(tcx, &new_substs, trait_ty);
debug!("after subst: %?",
ty_to_str(tcx, trait_ty));
match lookup_vtable(vcx, location_info, *ty, trait_ty,
allow_unsafe, is_early) {
Some(vtable) => result.push(vtable),
None => {
vcx.tcx().sess.span_fatal(
location_info.span,
fmt!("failed to find an implementation of \
trait %s for %s",
ty_to_str(vcx.tcx(), trait_ty),
ty_to_str(vcx.tcx(), *ty)));
}
}
_ => ()
}
}
i += 1u;
}
debug!("lookup_vtables result(\
location_info=%?,
# bounds=%?, \
substs=%s, \
result=%?",
location_info,
bounds.len(),
ty::substs_to_str(vcx.tcx(), substs),
result);
@result
}
@ -112,32 +127,15 @@ fn relate_trait_tys(vcx: &VtableContext, location_info: &LocationInfo,
}
// Look up the vtable to use when treating an item of type `t` as if it has
// type `trait_ty`. This does allow subtraits.
fn lookup_vtable_covariant(vcx: &VtableContext,
location_info: &LocationInfo,
ty: ty::t,
trait_ty: ty::t,
allow_unsafe: bool,
is_early: bool)
-> Option<vtable_origin> {
debug!("lookup_vtable_covariant(ty: %s, trait_ty=%s)",
vcx.infcx.ty_to_str(ty),
vcx.infcx.ty_to_str(trait_ty));
lookup_vtable_invariant(vcx, location_info, ty, trait_ty,
allow_unsafe, is_early)
}
// Look up the vtable to use when treating an item of type `t` as if it has
// type `trait_ty`. This does not allow subtraits.
fn lookup_vtable_invariant(vcx: &VtableContext,
location_info: &LocationInfo,
ty: ty::t,
trait_ty: ty::t,
allow_unsafe: bool,
is_early: bool)
-> Option<vtable_origin> {
debug!("lookup_vtable_invariant(ty=%s, trait_ty=%s)",
// type `trait_ty`
fn lookup_vtable(vcx: &VtableContext,
location_info: &LocationInfo,
ty: ty::t,
trait_ty: ty::t,
allow_unsafe: bool,
is_early: bool)
-> Option<vtable_origin> {
debug!("lookup_vtable(ty=%s, trait_ty=%s)",
vcx.infcx.ty_to_str(ty), vcx.infcx.ty_to_str(trait_ty));
let _i = indenter();
@ -145,7 +143,7 @@ fn lookup_vtable_invariant(vcx: &VtableContext,
let (trait_id, trait_substs, trait_vstore) = match ty::get(trait_ty).sty {
ty::ty_trait(did, substs, vstore) => (did, substs, vstore),
_ => tcx.sess.impossible_case(location_info.span,
"lookup_vtable_invariant: \
"lookup_vtable: \
don't know how to handle a non-trait")
};
let ty = match fixup_ty(vcx, location_info, ty, is_early) {
@ -163,32 +161,35 @@ fn lookup_vtable_invariant(vcx: &VtableContext,
match ty::get(ty).sty {
ty::ty_param({idx: n, def_id: did}) => {
let mut n_bound = 0;
for vec::each(*tcx.ty_param_bounds.get(did.node)) |bound| {
match *bound {
ty::bound_send | ty::bound_copy | ty::bound_const |
ty::bound_owned => {
/* ignore */
}
ty::bound_trait(ity) => {
match ty::get(ity).sty {
ty::ty_trait(idid, _, _) => {
if trait_id == idid {
debug!("(checking vtable) @0 relating \
ty to trait ty with did %?",
idid);
relate_trait_tys(vcx, location_info,
trait_ty, ity);
return Some(vtable_param(n, n_bound));
}
}
_ => tcx.sess.impossible_case(
location_info.span,
"lookup_vtable_invariant: in loop, \
don't know how to handle a non-trait ity")
let bounds = tcx.ty_param_bounds.get(did.node);
for ty::iter_bound_traits_and_supertraits(
tcx, bounds) |ity| {
debug!("checking bounds trait %?",
vcx.infcx.ty_to_str(ity));
match ty::get(ity).sty {
ty::ty_trait(idid, _, _) => {
if trait_id == idid {
debug!("(checking vtable) @0 \
relating ty to trait \
ty with did %?",
idid);
relate_trait_tys(vcx, location_info,
trait_ty, ity);
let vtable = vtable_param(n, n_bound);
debug!("found param vtable: %?",
vtable);
return Some(vtable);
}
n_bound += 1u;
}
_ => tcx.sess.impossible_case(
location_info.span,
"lookup_vtable: in loop, \
don't know how to handle a \
non-trait ity")
}
n_bound += 1;
}
}
@ -283,8 +284,6 @@ fn lookup_vtable_invariant(vcx: &VtableContext,
// impl.
let {substs: substs, ty: for_ty} =
impl_self_ty(vcx, location_info, im.did);
let im_bs = ty::lookup_item_type(tcx,
im.did).bounds;
match infer::mk_subty(vcx.infcx,
false,
location_info.span,
@ -369,6 +368,8 @@ fn lookup_vtable_invariant(vcx: &VtableContext,
// to. connect_trait_tps requires these
// lists of types to unify pairwise.
let im_bs = ty::lookup_item_type(tcx,
im.did).bounds;
connect_trait_tps(vcx,
location_info,
substs_f.tps,
@ -493,8 +494,9 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
Some(ref substs) => {
let def = cx.tcx.def_map.get(ex.id);
let did = ast_util::def_id_of_def(def);
debug!("early resolve expr: def %?", def);
let item_ty = ty::lookup_item_type(cx.tcx, did);
debug!("early resolve expr: def %? %?, %?, %?", ex.id, did, def,
fcx.infcx().ty_to_str(item_ty.ty));
if has_trait_bounds(*item_ty.bounds) {
for item_ty.bounds.each |bounds| {
debug!("early_resolve_expr: looking up vtables for bound \
@ -527,6 +529,7 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
ast::expr_field(_, _, _) => ex.id,
_ => ex.callee_id
};
let substs = fcx.node_ty_substs(callee_id);
let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() };
let vtbls = lookup_vtables(&vcx, &location_info_for_expr(ex),
@ -551,12 +554,12 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
let ty = fcx.expr_ty(src);
let vcx = VtableContext { ccx: fcx.ccx, infcx: fcx.infcx() };
let vtable_opt =
lookup_vtable_invariant(&vcx,
&location_info_for_expr(ex),
ty,
target_ty,
true,
is_early);
lookup_vtable(&vcx,
&location_info_for_expr(ex),
ty,
target_ty,
true,
is_early);
match vtable_opt {
None => {
// Try the new-style boxed trait; "@int as @Trait".
@ -577,12 +580,12 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
let location_info =
&location_info_for_expr(ex);
let vtable_opt =
lookup_vtable_invariant(&vcx,
location_info,
mt.ty,
target_ty,
true,
is_early);
lookup_vtable(&vcx,
location_info,
mt.ty,
target_ty,
true,
is_early);
match vtable_opt {
Some(vtable) => {
// Map this expression to that