Refactor a bunch of lookup_vtable to reduce rightward drift and clean things up.

This commit is contained in:
Michael Sullivan 2013-07-18 17:20:58 -07:00
parent 4b9759e20f
commit fbbbc98ea4
3 changed files with 245 additions and 225 deletions

View File

@ -1109,7 +1109,9 @@ pub fn find_vtable(tcx: ty::ctxt,
debug!("find_vtable(n_param=%u, n_bound=%u, ps=%s)",
n_param, n_bound, ps.repr(tcx));
ps.vtables.get()[n_param][n_bound].clone()
let tables = ps.vtables.expect("vtables missing where they are needed");
let param_bounds = tables[n_param];
param_bounds[n_bound].clone()
}
pub fn dummy_substs(tps: ~[ty::t]) -> ty::substs {

View File

@ -16,11 +16,10 @@ use middle::typeck::check::{structurally_resolved_type};
use middle::typeck::infer::fixup_err_to_str;
use middle::typeck::infer::{resolve_and_force_all_but_regions, resolve_type};
use middle::typeck::infer;
use middle::typeck::{CrateCtxt, vtable_origin, vtable_res};
use middle::typeck::{CrateCtxt, vtable_origin, vtable_res, vtable_param_res};
use middle::typeck::{vtable_static, vtable_param, vtable_self};
use middle::subst::Subst;
use util::common::indenter;
use util::ppaux::tys_to_str;
use util::ppaux;
use std::hashmap::HashSet;
@ -84,41 +83,10 @@ fn lookup_vtables(vcx: &VtableContext,
substs.repr(vcx.tcx()));
let _i = indenter();
let tcx = vcx.tcx();
let mut result = ~[];
let mut i = 0u;
for substs.tps.iter().advance |ty| {
// ty is the value supplied for the type parameter A...
let mut param_result = ~[];
for ty::each_bound_trait_and_supertraits(
tcx, type_param_defs[i].bounds) |trait_ref|
{
// ...and here trait_ref is each bound that was declared on A,
// expressed in terms of the type parameters.
debug!("about to subst: %s, %s", trait_ref.repr(tcx), substs.repr(tcx));
// Substitute the values of the type parameters that may
// appear in the bound.
let trait_ref = trait_ref.subst(tcx, substs);
debug!("after subst: %s", trait_ref.repr(tcx));
match lookup_vtable(vcx, location_info, *ty, trait_ref, is_early) {
Some(vtable) => param_result.push(vtable),
None => {
vcx.tcx().sess.span_fatal(
location_info.span,
fmt!("failed to find an implementation of \
trait %s for %s",
vcx.infcx.trait_ref_to_str(trait_ref),
vcx.infcx.ty_to_str(*ty)));
}
}
}
result.push(@param_result);
i += 1u;
for substs.tps.iter().zip(type_param_defs.iter()).advance |(ty, def)| {
result.push(lookup_vtables_for_param(vcx, location_info, Some(substs),
&*def.bounds, *ty, is_early));
}
debug!("lookup_vtables result(\
location_info=%?, \
@ -132,25 +100,58 @@ fn lookup_vtables(vcx: &VtableContext,
@result
}
fn fixup_substs(vcx: &VtableContext,
location_info: &LocationInfo,
id: ast::def_id,
substs: ty::substs,
is_early: bool)
-> Option<ty::substs> {
fn lookup_vtables_for_param(vcx: &VtableContext,
location_info: &LocationInfo,
// None for substs means the identity
substs: Option<&ty::substs>,
type_param_bounds: &ty::ParamBounds,
ty: ty::t,
is_early: bool) -> vtable_param_res {
let tcx = vcx.tcx();
// use a dummy type just to package up the substs that need fixing up
let t = ty::mk_trait(tcx,
id, substs,
ty::RegionTraitStore(ty::re_static),
ast::m_imm,
ty::EmptyBuiltinBounds());
do fixup_ty(vcx, location_info, t, is_early).map |t_f| {
match ty::get(*t_f).sty {
ty::ty_trait(_, ref substs_f, _, _, _) => (*substs_f).clone(),
_ => fail!("t_f should be a trait")
// ty is the value supplied for the type parameter A...
let mut param_result = ~[];
for ty::each_bound_trait_and_supertraits(
tcx, type_param_bounds) |trait_ref|
{
// ...and here trait_ref is each bound that was declared on A,
// expressed in terms of the type parameters.
// Substitute the values of the type parameters that may
// appear in the bound.
let trait_ref = substs.map_default(trait_ref, |substs| {
debug!("about to subst: %s, %s",
trait_ref.repr(tcx), substs.repr(tcx));
trait_ref.subst(tcx, *substs)
});
debug!("after subst: %s", trait_ref.repr(tcx));
match lookup_vtable(vcx, location_info, ty, trait_ref, is_early) {
Some(vtable) => param_result.push(vtable),
None => {
vcx.tcx().sess.span_fatal(
location_info.span,
fmt!("failed to find an implementation of \
trait %s for %s",
vcx.infcx.trait_ref_to_str(trait_ref),
vcx.infcx.ty_to_str(ty)));
}
}
}
debug!("lookup_vtables_for_param result(\
location_info=%?, \
type_param_bounds=%s, \
ty=%s, \
result=%s)",
location_info,
type_param_bounds.repr(vcx.tcx()),
ty.repr(vcx.tcx()),
param_result.repr(vcx.tcx()));
return @param_result;
}
fn relate_trait_refs(vcx: &VtableContext,
@ -173,10 +174,15 @@ fn relate_trait_refs(vcx: &VtableContext,
{
result::Ok(()) => {} // Ok.
result::Err(ref err) => {
// There is an error, but we need to do some work to make
// the message good.
// Resolve any type vars in the trait refs
let r_act_trait_ref =
vcx.infcx.resolve_type_vars_in_trait_ref_if_possible(act_trait_ref);
let r_exp_trait_ref =
vcx.infcx.resolve_type_vars_in_trait_ref_if_possible(exp_trait_ref);
// Only print the message if there aren't any previous type errors
// inside the types.
if !ty::trait_ref_contains_error(&r_act_trait_ref) &&
!ty::trait_ref_contains_error(&r_exp_trait_ref)
{
@ -256,182 +262,186 @@ fn lookup_vtable(vcx: &VtableContext,
}
_ => {
let mut found = ~[];
let mut impls_seen = HashSet::new();
match tcx.trait_impls.find(&trait_ref.def_id) {
None => {
// Nothing found. Continue.
}
Some(implementations) => {
// implementations is the list of all impls in scope for
// trait_ref. (Usually, there's just one.)
for implementations.iter().advance |im| {
// im is one specific impl of trait_ref.
// First, ensure we haven't processed this impl yet.
if impls_seen.contains(&im.did) {
loop;
}
impls_seen.insert(im.did);
// ty::impl_traits gives us the trait im implements,
// if there is one (there's either zero or one).
//
// If foo implements a trait t, and if t is the
// same trait as trait_ref, we need to
// unify it with trait_ref in order to get all
// the ty vars sorted out.
let r = ty::impl_trait_ref(tcx, im.did);
for r.iter().advance |&of_trait_ref| {
if of_trait_ref.def_id != trait_ref.def_id { loop; }
// At this point, we know that of_trait_ref is
// the same trait as trait_ref, but
// possibly applied to different substs.
//
// Next, we check whether the "for" ty in
// the impl is compatible with the type
// that we're casting to a trait. That is,
// if im is:
//
// impl<T> self_ty<T>: some_trait<T> { ... }
//
// we check whether self_ty<T> is the type
// of the thing that we're trying to cast
// to some_trait. If not, then we try the next
// impl.
//
// FIXME(#5781) this should be mk_eqty not mk_subty
let ty::ty_param_substs_and_ty {
substs: substs,
ty: for_ty
} = impl_self_ty(vcx, location_info, im.did);
match infer::mk_subty(vcx.infcx,
false,
infer::RelateSelfType(
location_info.span),
ty,
for_ty) {
result::Err(_) => loop,
result::Ok(()) => ()
}
// Now, in the previous example, for_ty is
// bound to the type self_ty, and substs
// is bound to [T].
debug!("The self ty is %s and its substs are %s",
vcx.infcx.ty_to_str(for_ty),
tys_to_str(vcx.tcx(), substs.tps));
// Next, we unify trait_ref -- the type
// that we want to cast to -- with of_trait_ref
// -- the trait that im implements. At
// this point, we require that they be
// unifiable with each other -- that's
// what relate_trait_refs does.
//
// For example, in the above example,
// of_trait_ref would be some_trait<T>, so we
// would be unifying trait_ref<U> (for some
// value of U) with some_trait<T>. This
// would fail if T and U weren't
// compatible.
debug!("(checking vtable) @2 relating trait \
ty %s to of_trait_ref %s",
vcx.infcx.trait_ref_to_str(trait_ref),
vcx.infcx.trait_ref_to_str(of_trait_ref));
let of_trait_ref = of_trait_ref.subst(tcx, &substs);
relate_trait_refs(
vcx, location_info,
of_trait_ref, trait_ref);
// Recall that trait_ref -- the trait type
// we're casting to -- is the trait with
// id trait_ref.def_id applied to the substs
// trait_ref.substs. Now we extract out the
// types themselves from trait_ref.substs.
// Recall that substs is the impl self
// type's list of substitutions. That is,
// if this is an impl of some trait for
// foo<T, U>, then substs is [T,
// U]. substs might contain type
// variables, so we call fixup_substs to
// resolve them.
let substs_f = match fixup_substs(vcx,
location_info,
trait_ref.def_id,
substs,
is_early) {
Some(ref substs) => (*substs).clone(),
None => {
assert!(is_early);
// Bail out with a bogus answer
return Some(vtable_param(0, 0));
}
};
debug!("The fixed-up substs are %s - \
they will be unified with the bounds for \
the target ty, %s",
tys_to_str(vcx.tcx(), substs_f.tps),
vcx.infcx.trait_ref_to_str(trait_ref));
// Next, we unify the fixed-up
// substitutions for the impl self ty with
// the substitutions from the trait type
// that we're trying to cast
// to. connect_trait_tps requires these
// lists of types to unify pairwise.
let im_generics =
ty::lookup_item_type(tcx, im.did).generics;
connect_trait_tps(vcx,
location_info,
&substs_f,
trait_ref,
im.did);
let subres = lookup_vtables(
vcx, location_info,
*im_generics.type_param_defs, &substs_f,
is_early);
// Finally, we register that we found a
// matching impl, and record the def ID of
// the impl as well as the resolved list
// of type substitutions for the target
// trait.
found.push(vtable_static(im.did,
substs_f.tps.clone(),
subres));
}
}
}
}
match found.len() {
0 => { /* fallthrough */ }
1 => return Some(found[0].clone()),
_ => {
if !is_early {
vcx.tcx().sess.span_err(
location_info.span,
"multiple applicable methods in scope");
}
return Some(found[0].clone());
}
}
return search_for_vtable(vcx, location_info,
ty, trait_ref, is_early)
}
}
return None;
}
fn search_for_vtable(vcx: &VtableContext,
location_info: &LocationInfo,
ty: ty::t,
trait_ref: @ty::TraitRef,
is_early: bool)
-> Option<vtable_origin>
{
let tcx = vcx.tcx();
let mut found = ~[];
let mut impls_seen = HashSet::new();
// XXX: this is a bad way to do this, since we do
// pointless allocations.
let impls = tcx.trait_impls.find(&trait_ref.def_id)
.map_default(@mut ~[], |x| **x);
// impls is the list of all impls in scope for trait_ref.
for impls.iter().advance |im| {
// im is one specific impl of trait_ref.
// First, ensure we haven't processed this impl yet.
if impls_seen.contains(&im.did) {
loop;
}
impls_seen.insert(im.did);
// ty::impl_traits gives us the trait im implements.
//
// If foo implements a trait t, and if t is the same trait as
// trait_ref, we need to unify it with trait_ref in order to
// get all the ty vars sorted out.
let r = ty::impl_trait_ref(tcx, im.did);
let of_trait_ref = r.expect("trait_ref missing on trait impl");
if of_trait_ref.def_id != trait_ref.def_id { loop; }
// At this point, we know that of_trait_ref is the same trait
// as trait_ref, but possibly applied to different substs.
//
// Next, we check whether the "for" ty in the impl is
// compatible with the type that we're casting to a
// trait. That is, if im is:
//
// impl<T> some_trait<T> for self_ty<T> { ... }
//
// we check whether self_ty<T> is the type of the thing that
// we're trying to cast to some_trait. If not, then we try
// the next impl.
//
// XXX: document a bit more what this means
//
// FIXME(#5781) this should be mk_eqty not mk_subty
let ty::ty_param_substs_and_ty {
substs: substs,
ty: for_ty
} = impl_self_ty(vcx, location_info, im.did);
match infer::mk_subty(vcx.infcx,
false,
infer::RelateSelfType(
location_info.span),
ty,
for_ty) {
result::Err(_) => loop,
result::Ok(()) => ()
}
// Now, in the previous example, for_ty is bound to
// the type self_ty, and substs is bound to [T].
debug!("The self ty is %s and its substs are %s",
vcx.infcx.ty_to_str(for_ty),
vcx.infcx.tys_to_str(substs.tps));
// Next, we unify trait_ref -- the type that we want to cast
// to -- with of_trait_ref -- the trait that im implements. At
// this point, we require that they be unifiable with each
// other -- that's what relate_trait_refs does.
//
// For example, in the above example, of_trait_ref would be
// some_trait<T>, so we would be unifying trait_ref<U> (for
// some value of U) with some_trait<T>. This would fail if T
// and U weren't compatible.
debug!("(checking vtable) @2 relating trait \
ty %s to of_trait_ref %s",
vcx.infcx.trait_ref_to_str(trait_ref),
vcx.infcx.trait_ref_to_str(of_trait_ref));
let of_trait_ref = of_trait_ref.subst(tcx, &substs);
relate_trait_refs(vcx, location_info, of_trait_ref, trait_ref);
// Recall that trait_ref -- the trait type we're casting to --
// is the trait with id trait_ref.def_id applied to the substs
// trait_ref.substs. Now we extract out the types themselves
// from trait_ref.substs.
// Recall that substs is the impl self type's list of
// substitutions. That is, if this is an impl of some trait
// for foo<T, U>, then substs is [T, U]. substs might contain
// type variables, so we call fixup_substs to resolve them.
let substs_f = match fixup_substs(vcx,
location_info,
trait_ref.def_id,
substs,
is_early) {
Some(ref substs) => (*substs).clone(),
None => {
assert!(is_early);
// Bail out with a bogus answer
return Some(vtable_param(0, 0));
}
};
debug!("The fixed-up substs are %s - \
they will be unified with the bounds for \
the target ty, %s",
vcx.infcx.tys_to_str(substs_f.tps),
vcx.infcx.trait_ref_to_str(trait_ref));
// Next, we unify the fixed-up substitutions for the impl self
// ty with the substitutions from the trait type that we're
// trying to cast to. connect_trait_tps requires these lists
// of types to unify pairwise.
let im_generics =
ty::lookup_item_type(tcx, im.did).generics;
connect_trait_tps(vcx, location_info, &substs_f, trait_ref, im.did);
let subres = lookup_vtables(vcx, location_info,
*im_generics.type_param_defs, &substs_f,
is_early);
// Finally, we register that we found a matching impl, and
// record the def ID of the impl as well as the resolved list
// of type substitutions for the target trait.
found.push(vtable_static(im.did, substs_f.tps.clone(), subres));
}
match found.len() {
0 => { return None }
1 => return Some(found[0].clone()),
_ => {
if !is_early {
vcx.tcx().sess.span_err(
location_info.span,
"multiple applicable methods in scope");
}
return Some(found[0].clone());
}
}
}
fn fixup_substs(vcx: &VtableContext,
location_info: &LocationInfo,
id: ast::def_id,
substs: ty::substs,
is_early: bool)
-> Option<ty::substs> {
let tcx = vcx.tcx();
// use a dummy type just to package up the substs that need fixing up
let t = ty::mk_trait(tcx,
id, substs,
ty::RegionTraitStore(ty::re_static),
ast::m_imm,
ty::EmptyBuiltinBounds());
do fixup_ty(vcx, location_info, t, is_early).map |t_f| {
match ty::get(*t_f).sty {
ty::ty_trait(_, ref substs_f, _, _, _) => (*substs_f).clone(),
_ => fail!("t_f should be a trait")
}
}
}
fn fixup_ty(vcx: &VtableContext,
location_info: &LocationInfo,
ty: ty::t,
@ -682,10 +692,13 @@ pub fn resolve_impl(ccx: @mut CrateCtxt, impl_item: @ast::item) {
Some(trait_ref) => {
let infcx = infer::new_infer_ctxt(ccx.tcx);
let vcx = VtableContext { ccx: ccx, infcx: infcx };
let trait_def = ty::lookup_trait_def(ccx.tcx, trait_ref.def_id);
let loc_info = location_info_for_item(impl_item);
// First, check that the impl implements any trait bounds
// on the trait.
let trait_def = ty::lookup_trait_def(ccx.tcx, trait_ref.def_id);
let vtbls = lookup_vtables(&vcx,
&location_info_for_item(impl_item),
&loc_info,
*trait_def.generics.type_param_defs,
&trait_ref.substs,
false);

View File

@ -653,6 +653,11 @@ impl InferCtxt {
self.resolve_type_vars_if_possible(t))
}
pub fn tys_to_str(@mut self, ts: &[ty::t]) -> ~str {
let tstrs = ts.map(|t| self.ty_to_str(*t));
fmt!("(%s)", tstrs.connect(", "))
}
pub fn trait_ref_to_str(@mut self, t: &ty::TraitRef) -> ~str {
let t = self.resolve_type_vars_in_trait_ref_if_possible(t);
trait_ref_to_str(self.tcx, &t)