Try to do some resolution of vtables earlier, in a fairly ad-hoc way. Closes #3156.
This commit is contained in:
parent
bd736a0f9b
commit
71ec545614
3 changed files with 107 additions and 36 deletions
|
@ -820,6 +820,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
// overloaded operations
|
// overloaded operations
|
||||||
fn check_call_inner(
|
fn check_call_inner(
|
||||||
fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, in_fty: ty::t,
|
fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, in_fty: ty::t,
|
||||||
|
callee_expr: @ast::expr,
|
||||||
args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} {
|
args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} {
|
||||||
|
|
||||||
let mut bot = false;
|
let mut bot = false;
|
||||||
|
@ -888,9 +889,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
// of arguments when we typecheck the functions. This isn't really the
|
// of arguments when we typecheck the functions. This isn't really the
|
||||||
// right way to do this.
|
// right way to do this.
|
||||||
for [false, true]/_.each |check_blocks| {
|
for [false, true]/_.each |check_blocks| {
|
||||||
|
// More awful hacks: before we check the blocks, try to do
|
||||||
|
// an "opportunistic" vtable resolution of any trait
|
||||||
|
// bounds on the call.
|
||||||
|
if check_blocks {
|
||||||
|
vtable::early_resolve_expr(callee_expr, fcx, true);
|
||||||
|
}
|
||||||
|
|
||||||
for args.eachi |i, a| {
|
for args.eachi |i, a| {
|
||||||
let is_block = match a.node {
|
let is_block = match a.node {
|
||||||
ast::expr_fn_block(*) => true,
|
ast::expr_fn_block(*) | ast::expr_loop_body(*) |
|
||||||
|
ast::expr_do_body(*) => true,
|
||||||
_ => false
|
_ => false
|
||||||
};
|
};
|
||||||
if is_block == check_blocks {
|
if is_block == check_blocks {
|
||||||
|
@ -933,7 +942,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
// Call the generic checker.
|
// Call the generic checker.
|
||||||
let fty = {
|
let fty = {
|
||||||
let r = check_call_inner(fcx, sp, call_expr_id,
|
let r = check_call_inner(fcx, sp, call_expr_id,
|
||||||
fn_ty, args);
|
fn_ty, f, args);
|
||||||
bot |= r.bot;
|
bot |= r.bot;
|
||||||
r.fty
|
r.fty
|
||||||
};
|
};
|
||||||
|
@ -998,7 +1007,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
let {fty: method_ty, bot: bot} = {
|
let {fty: method_ty, bot: bot} = {
|
||||||
let method_ty = fcx.node_ty(op_ex.callee_id);
|
let method_ty = fcx.node_ty(op_ex.callee_id);
|
||||||
check_call_inner(fcx, op_ex.span, op_ex.id,
|
check_call_inner(fcx, op_ex.span, op_ex.id,
|
||||||
method_ty, args)
|
method_ty, op_ex, args)
|
||||||
};
|
};
|
||||||
fcx.ccx.method_map.insert(op_ex.id, origin);
|
fcx.ccx.method_map.insert(op_ex.id, origin);
|
||||||
some((ty::ty_fn_ret(method_ty), bot))
|
some((ty::ty_fn_ret(method_ty), bot))
|
||||||
|
|
|
@ -2,6 +2,22 @@ import check::{fn_ctxt, impl_self_ty};
|
||||||
import infer::{resolve_type, resolve_all, force_all, fixup_err_to_str};
|
import infer::{resolve_type, resolve_all, force_all, fixup_err_to_str};
|
||||||
import ast_util::new_def_hash;
|
import ast_util::new_def_hash;
|
||||||
|
|
||||||
|
// vtable resolution looks for places where trait bounds are
|
||||||
|
// subsituted in and figures out which vtable is used. There is some
|
||||||
|
// extra complication thrown in to support early "opportunistic"
|
||||||
|
// vtable resolution. This is a hacky mechanism that is invoked while
|
||||||
|
// typechecking function calls (after typechecking non-closure
|
||||||
|
// arguments and before typechecking closure arguments) in the hope of
|
||||||
|
// solving for the trait parameters from the impl. (For example,
|
||||||
|
// determining that if a parameter bounded by BaseIter<A> is
|
||||||
|
// instantiated with option<int>, that A = int.)
|
||||||
|
//
|
||||||
|
// In early resolution mode, no vtables are recorded, and a number of
|
||||||
|
// errors are ignored. Early resolution only works if a type is
|
||||||
|
// *fully* resolved. (We could be less restrictive than that, but it
|
||||||
|
// would require much more care, and this seems to work decently in
|
||||||
|
// practice.)
|
||||||
|
|
||||||
fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
|
fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
|
||||||
vec::any(tps, |bs| {
|
vec::any(tps, |bs| {
|
||||||
vec::any(*bs, |b| {
|
vec::any(*bs, |b| {
|
||||||
|
@ -14,7 +30,8 @@ fn lookup_vtables(fcx: @fn_ctxt,
|
||||||
sp: span,
|
sp: span,
|
||||||
bounds: @~[ty::param_bounds],
|
bounds: @~[ty::param_bounds],
|
||||||
substs: &ty::substs,
|
substs: &ty::substs,
|
||||||
allow_unsafe: bool) -> vtable_res {
|
allow_unsafe: bool,
|
||||||
|
is_early: bool) -> vtable_res {
|
||||||
let tcx = fcx.ccx.tcx;
|
let tcx = fcx.ccx.tcx;
|
||||||
let mut result = ~[], i = 0u;
|
let mut result = ~[], i = 0u;
|
||||||
for substs.tps.each |ty| {
|
for substs.tps.each |ty| {
|
||||||
|
@ -23,7 +40,7 @@ fn lookup_vtables(fcx: @fn_ctxt,
|
||||||
ty::bound_trait(i_ty) => {
|
ty::bound_trait(i_ty) => {
|
||||||
let i_ty = ty::subst(tcx, substs, i_ty);
|
let i_ty = ty::subst(tcx, substs, i_ty);
|
||||||
vec::push(result, lookup_vtable(fcx, sp, ty, i_ty,
|
vec::push(result, lookup_vtable(fcx, sp, ty, i_ty,
|
||||||
allow_unsafe));
|
allow_unsafe, is_early));
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
@ -34,13 +51,15 @@ fn lookup_vtables(fcx: @fn_ctxt,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixup_substs(fcx: @fn_ctxt, sp: span,
|
fn fixup_substs(fcx: @fn_ctxt, sp: span,
|
||||||
id: ast::def_id, substs: ty::substs) -> ty::substs {
|
id: ast::def_id, substs: ty::substs,
|
||||||
|
is_early: bool) -> option<ty::substs> {
|
||||||
let tcx = fcx.ccx.tcx;
|
let tcx = fcx.ccx.tcx;
|
||||||
// use a dummy type just to package up the substs that need fixing up
|
// use a dummy type just to package up the substs that need fixing up
|
||||||
let t = ty::mk_trait(tcx, id, substs, ty::vstore_slice(ty::re_static));
|
let t = ty::mk_trait(tcx, id, substs, ty::vstore_slice(ty::re_static));
|
||||||
let t_f = fixup_ty(fcx, sp, t);
|
do fixup_ty(fcx, sp, t, is_early).map |t_f| {
|
||||||
match check ty::get(t_f).struct {
|
match check ty::get(t_f).struct {
|
||||||
ty::ty_trait(_, substs_f, _) => substs_f,
|
ty::ty_trait(_, substs_f, _) => substs_f,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +73,7 @@ Look up the vtable to use when treating an item of type <t>
|
||||||
as if it has type <trait_ty>
|
as if it has type <trait_ty>
|
||||||
*/
|
*/
|
||||||
fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||||
allow_unsafe: bool)
|
allow_unsafe: bool, is_early: bool)
|
||||||
-> vtable_origin {
|
-> vtable_origin {
|
||||||
|
|
||||||
debug!{"lookup_vtable(ty=%s, trait_ty=%s)",
|
debug!{"lookup_vtable(ty=%s, trait_ty=%s)",
|
||||||
|
@ -65,7 +84,18 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||||
let (trait_id, trait_substs) = match check ty::get(trait_ty).struct {
|
let (trait_id, trait_substs) = match check ty::get(trait_ty).struct {
|
||||||
ty::ty_trait(did, substs, _) => (did, substs)
|
ty::ty_trait(did, substs, _) => (did, substs)
|
||||||
};
|
};
|
||||||
let ty = fixup_ty(fcx, sp, ty);
|
let ty = match fixup_ty(fcx, sp, ty, is_early) {
|
||||||
|
some(ty) => ty,
|
||||||
|
none => {
|
||||||
|
// fixup_ty can only fail if this is early resolution
|
||||||
|
assert is_early;
|
||||||
|
// The type has unconstrained type variables in it, so we can't
|
||||||
|
// do early resolution on it. Return some completely bogus vtable
|
||||||
|
// information: we aren't storing it anyways.
|
||||||
|
return vtable_param(0, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match ty::get(ty).struct {
|
match ty::get(ty).struct {
|
||||||
ty::ty_param({idx: n, def_id: did}) => {
|
ty::ty_param({idx: n, def_id: did}) => {
|
||||||
let mut n_bound = 0u;
|
let mut n_bound = 0u;
|
||||||
|
@ -97,7 +127,7 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||||
did};
|
did};
|
||||||
|
|
||||||
relate_trait_tys(fcx, sp, trait_ty, ty);
|
relate_trait_tys(fcx, sp, trait_ty, ty);
|
||||||
if !allow_unsafe {
|
if !allow_unsafe && !is_early {
|
||||||
for vec::each(*ty::trait_methods(tcx, did)) |m| {
|
for vec::each(*ty::trait_methods(tcx, did)) |m| {
|
||||||
if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
|
if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
|
||||||
tcx.sess.span_err(
|
tcx.sess.span_err(
|
||||||
|
@ -154,21 +184,30 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that desired trait type unifies
|
// check that desired trait type unifies
|
||||||
debug!{"(checking vtable) @2 relating trait ty %s to \
|
debug!("(checking vtable) @2 relating trait ty %s to \
|
||||||
of_ty %s",
|
of_ty %s",
|
||||||
fcx.infcx.ty_to_str(trait_ty),
|
fcx.infcx.ty_to_str(trait_ty),
|
||||||
fcx.infcx.ty_to_str(of_ty)};
|
fcx.infcx.ty_to_str(of_ty));
|
||||||
let of_ty = ty::subst(tcx, &substs, of_ty);
|
let of_ty = ty::subst(tcx, &substs, of_ty);
|
||||||
relate_trait_tys(fcx, sp, trait_ty, of_ty);
|
relate_trait_tys(fcx, sp, trait_ty, of_ty);
|
||||||
|
|
||||||
// recursively process the bounds
|
// recursively process the bounds.
|
||||||
let trait_tps = trait_substs.tps;
|
let trait_tps = trait_substs.tps;
|
||||||
let substs_f = fixup_substs(fcx, sp, trait_id,
|
// see comments around the earlier call to fixup_ty
|
||||||
substs);
|
let substs_f = match fixup_substs(fcx, sp, trait_id,
|
||||||
|
substs, is_early) {
|
||||||
|
some(substs) => substs,
|
||||||
|
none => {
|
||||||
|
assert is_early;
|
||||||
|
// Bail out with a bogus answer
|
||||||
|
return vtable_param(0, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
connect_trait_tps(fcx, sp, substs_f.tps,
|
connect_trait_tps(fcx, sp, substs_f.tps,
|
||||||
trait_tps, im.did);
|
trait_tps, im.did);
|
||||||
let subres = lookup_vtables(fcx, sp, im_bs, &substs_f,
|
let subres = lookup_vtables(fcx, sp, im_bs, &substs_f,
|
||||||
false);
|
false, is_early);
|
||||||
vec::push(found,
|
vec::push(found,
|
||||||
vtable_static(im.did, substs_f.tps,
|
vtable_static(im.did, substs_f.tps,
|
||||||
subres));
|
subres));
|
||||||
|
@ -181,8 +220,10 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||||
0u => { /* fallthrough */ }
|
0u => { /* fallthrough */ }
|
||||||
1u => { return found[0]; }
|
1u => { return found[0]; }
|
||||||
_ => {
|
_ => {
|
||||||
fcx.ccx.tcx.sess.span_err(
|
if !is_early {
|
||||||
sp, ~"multiple applicable methods in scope");
|
fcx.ccx.tcx.sess.span_err(
|
||||||
|
sp, ~"multiple applicable methods in scope");
|
||||||
|
}
|
||||||
return found[0];
|
return found[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,17 +236,21 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
|
||||||
ty_to_str(tcx, ty));
|
ty_to_str(tcx, ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
|
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t, is_early: bool)
|
||||||
|
-> option<ty::t> {
|
||||||
let tcx = fcx.ccx.tcx;
|
let tcx = fcx.ccx.tcx;
|
||||||
match resolve_type(fcx.infcx, ty, resolve_all | force_all) {
|
match resolve_type(fcx.infcx, ty, resolve_all | force_all) {
|
||||||
result::ok(new_type) => new_type,
|
result::ok(new_type) => some(new_type),
|
||||||
result::err(e) => {
|
result::err(e) if !is_early => {
|
||||||
tcx.sess.span_fatal(
|
tcx.sess.span_fatal(
|
||||||
sp,
|
sp,
|
||||||
fmt!{"cannot determine a type \
|
fmt!{"cannot determine a type \
|
||||||
for this bounded type parameter: %s",
|
for this bounded type parameter: %s",
|
||||||
fixup_err_to_str(e)})
|
fixup_err_to_str(e)})
|
||||||
}
|
}
|
||||||
|
result::err(e) => {
|
||||||
|
none
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +271,7 @@ fn connect_trait_tps(fcx: @fn_ctxt, sp: span, impl_tys: ~[ty::t],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
|
fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||||
let cx = fcx.ccx;
|
let cx = fcx.ccx;
|
||||||
match ex.node {
|
match ex.node {
|
||||||
ast::expr_path(*) => {
|
ast::expr_path(*) => {
|
||||||
|
@ -236,11 +281,9 @@ fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
|
||||||
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
|
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
|
||||||
let item_ty = ty::lookup_item_type(cx.tcx, did);
|
let item_ty = ty::lookup_item_type(cx.tcx, did);
|
||||||
if has_trait_bounds(*item_ty.bounds) {
|
if has_trait_bounds(*item_ty.bounds) {
|
||||||
cx.vtable_map.insert(ex.id, lookup_vtables(fcx,
|
let vtbls = lookup_vtables(fcx, ex.span, item_ty.bounds,
|
||||||
ex.span,
|
substs, false, is_early);
|
||||||
item_ty.bounds,
|
if !is_early { cx.vtable_map.insert(ex.id, vtbls); }
|
||||||
substs,
|
|
||||||
false));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
|
@ -260,11 +303,9 @@ fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
|
||||||
_ => ex.callee_id
|
_ => ex.callee_id
|
||||||
};
|
};
|
||||||
let substs = fcx.node_ty_substs(callee_id);
|
let substs = fcx.node_ty_substs(callee_id);
|
||||||
cx.vtable_map.insert(callee_id, lookup_vtables(fcx,
|
let vtbls = lookup_vtables(fcx, ex.span, bounds,
|
||||||
ex.span,
|
&substs, false, is_early);
|
||||||
bounds,
|
if !is_early { cx.vtable_map.insert(callee_id, vtbls); }
|
||||||
&substs,
|
|
||||||
false));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
|
@ -280,18 +321,22 @@ fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
|
||||||
passing in the source and target type
|
passing in the source and target type
|
||||||
*/
|
*/
|
||||||
let vtable = lookup_vtable(fcx, ex.span, fcx.expr_ty(src),
|
let vtable = lookup_vtable(fcx, ex.span, fcx.expr_ty(src),
|
||||||
target_ty, true);
|
target_ty, true, is_early);
|
||||||
/*
|
/*
|
||||||
Map this expression to that vtable (that is: "ex has
|
Map this expression to that vtable (that is: "ex has
|
||||||
vtable <vtable>")
|
vtable <vtable>")
|
||||||
*/
|
*/
|
||||||
cx.vtable_map.insert(ex.id, @~[vtable]);
|
if !is_early { cx.vtable_map.insert(ex.id, @~[vtable]); }
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
|
||||||
|
early_resolve_expr(ex, fcx, false);
|
||||||
visit::visit_expr(ex, fcx, v);
|
visit::visit_expr(ex, fcx, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
src/test/run-pass/early-vtbl-resolution.rs
Normal file
17
src/test/run-pass/early-vtbl-resolution.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
trait thing<A> {
|
||||||
|
fn foo() -> option<A>;
|
||||||
|
}
|
||||||
|
impl<A> int: thing<A> {
|
||||||
|
fn foo() -> option<A> { none }
|
||||||
|
}
|
||||||
|
fn foo_func<A, B: thing<A>>(x: B) -> option<A> { x.foo() }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
|
||||||
|
for iter::eachi(some({a: 0})) |i, a| {
|
||||||
|
#debug["%u %d", i, a.a];
|
||||||
|
}
|
||||||
|
|
||||||
|
let _x: option<float> = foo_func(0);
|
||||||
|
}
|
Loading…
Reference in a new issue