Skip to content

Commit 71ec545

Browse files
committed
Try to do some resolution of vtables earlier, in a fairly ad-hoc way. Closes #3156.
1 parent bd736a0 commit 71ec545

File tree

3 files changed

+107
-36
lines changed

3 files changed

+107
-36
lines changed

src/rustc/middle/typeck/check.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
820820
// overloaded operations
821821
fn check_call_inner(
822822
fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, in_fty: ty::t,
823+
callee_expr: @ast::expr,
823824
args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} {
824825

825826
let mut bot = false;
@@ -888,9 +889,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
888889
// of arguments when we typecheck the functions. This isn't really the
889890
// right way to do this.
890891
for [false, true]/_.each |check_blocks| {
892+
// More awful hacks: before we check the blocks, try to do
893+
// an "opportunistic" vtable resolution of any trait
894+
// bounds on the call.
895+
if check_blocks {
896+
vtable::early_resolve_expr(callee_expr, fcx, true);
897+
}
898+
891899
for args.eachi |i, a| {
892900
let is_block = match a.node {
893-
ast::expr_fn_block(*) => true,
901+
ast::expr_fn_block(*) | ast::expr_loop_body(*) |
902+
ast::expr_do_body(*) => true,
894903
_ => false
895904
};
896905
if is_block == check_blocks {
@@ -933,7 +942,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
933942
// Call the generic checker.
934943
let fty = {
935944
let r = check_call_inner(fcx, sp, call_expr_id,
936-
fn_ty, args);
945+
fn_ty, f, args);
937946
bot |= r.bot;
938947
r.fty
939948
};
@@ -998,7 +1007,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
9981007
let {fty: method_ty, bot: bot} = {
9991008
let method_ty = fcx.node_ty(op_ex.callee_id);
10001009
check_call_inner(fcx, op_ex.span, op_ex.id,
1001-
method_ty, args)
1010+
method_ty, op_ex, args)
10021011
};
10031012
fcx.ccx.method_map.insert(op_ex.id, origin);
10041013
some((ty::ty_fn_ret(method_ty), bot))

src/rustc/middle/typeck/check/vtable.rs

Lines changed: 78 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@ import check::{fn_ctxt, impl_self_ty};
22
import infer::{resolve_type, resolve_all, force_all, fixup_err_to_str};
33
import ast_util::new_def_hash;
44

5+
// vtable resolution looks for places where trait bounds are
6+
// subsituted in and figures out which vtable is used. There is some
7+
// extra complication thrown in to support early "opportunistic"
8+
// vtable resolution. This is a hacky mechanism that is invoked while
9+
// typechecking function calls (after typechecking non-closure
10+
// arguments and before typechecking closure arguments) in the hope of
11+
// solving for the trait parameters from the impl. (For example,
12+
// determining that if a parameter bounded by BaseIter<A> is
13+
// instantiated with option<int>, that A = int.)
14+
//
15+
// In early resolution mode, no vtables are recorded, and a number of
16+
// errors are ignored. Early resolution only works if a type is
17+
// *fully* resolved. (We could be less restrictive than that, but it
18+
// would require much more care, and this seems to work decently in
19+
// practice.)
20+
521
fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
622
vec::any(tps, |bs| {
723
vec::any(*bs, |b| {
@@ -14,7 +30,8 @@ fn lookup_vtables(fcx: @fn_ctxt,
1430
sp: span,
1531
bounds: @~[ty::param_bounds],
1632
substs: &ty::substs,
17-
allow_unsafe: bool) -> vtable_res {
33+
allow_unsafe: bool,
34+
is_early: bool) -> vtable_res {
1835
let tcx = fcx.ccx.tcx;
1936
let mut result = ~[], i = 0u;
2037
for substs.tps.each |ty| {
@@ -23,7 +40,7 @@ fn lookup_vtables(fcx: @fn_ctxt,
2340
ty::bound_trait(i_ty) => {
2441
let i_ty = ty::subst(tcx, substs, i_ty);
2542
vec::push(result, lookup_vtable(fcx, sp, ty, i_ty,
26-
allow_unsafe));
43+
allow_unsafe, is_early));
2744
}
2845
_ => ()
2946
}
@@ -34,13 +51,15 @@ fn lookup_vtables(fcx: @fn_ctxt,
3451
}
3552

3653
fn fixup_substs(fcx: @fn_ctxt, sp: span,
37-
id: ast::def_id, substs: ty::substs) -> ty::substs {
54+
id: ast::def_id, substs: ty::substs,
55+
is_early: bool) -> option<ty::substs> {
3856
let tcx = fcx.ccx.tcx;
3957
// use a dummy type just to package up the substs that need fixing up
4058
let t = ty::mk_trait(tcx, id, substs, ty::vstore_slice(ty::re_static));
41-
let t_f = fixup_ty(fcx, sp, t);
42-
match check ty::get(t_f).struct {
43-
ty::ty_trait(_, substs_f, _) => substs_f,
59+
do fixup_ty(fcx, sp, t, is_early).map |t_f| {
60+
match check ty::get(t_f).struct {
61+
ty::ty_trait(_, substs_f, _) => substs_f,
62+
}
4463
}
4564
}
4665

@@ -54,7 +73,7 @@ Look up the vtable to use when treating an item of type <t>
5473
as if it has type <trait_ty>
5574
*/
5675
fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
57-
allow_unsafe: bool)
76+
allow_unsafe: bool, is_early: bool)
5877
-> vtable_origin {
5978

6079
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,
6584
let (trait_id, trait_substs) = match check ty::get(trait_ty).struct {
6685
ty::ty_trait(did, substs, _) => (did, substs)
6786
};
68-
let ty = fixup_ty(fcx, sp, ty);
87+
let ty = match fixup_ty(fcx, sp, ty, is_early) {
88+
some(ty) => ty,
89+
none => {
90+
// fixup_ty can only fail if this is early resolution
91+
assert is_early;
92+
// The type has unconstrained type variables in it, so we can't
93+
// do early resolution on it. Return some completely bogus vtable
94+
// information: we aren't storing it anyways.
95+
return vtable_param(0, 0);
96+
}
97+
};
98+
6999
match ty::get(ty).struct {
70100
ty::ty_param({idx: n, def_id: did}) => {
71101
let mut n_bound = 0u;
@@ -97,7 +127,7 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
97127
did};
98128

99129
relate_trait_tys(fcx, sp, trait_ty, ty);
100-
if !allow_unsafe {
130+
if !allow_unsafe && !is_early {
101131
for vec::each(*ty::trait_methods(tcx, did)) |m| {
102132
if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
103133
tcx.sess.span_err(
@@ -154,21 +184,30 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
154184
}
155185

156186
// check that desired trait type unifies
157-
debug!{"(checking vtable) @2 relating trait ty %s to \
187+
debug!("(checking vtable) @2 relating trait ty %s to \
158188
of_ty %s",
159189
fcx.infcx.ty_to_str(trait_ty),
160-
fcx.infcx.ty_to_str(of_ty)};
190+
fcx.infcx.ty_to_str(of_ty));
161191
let of_ty = ty::subst(tcx, &substs, of_ty);
162192
relate_trait_tys(fcx, sp, trait_ty, of_ty);
163193

164-
// recursively process the bounds
194+
// recursively process the bounds.
165195
let trait_tps = trait_substs.tps;
166-
let substs_f = fixup_substs(fcx, sp, trait_id,
167-
substs);
196+
// see comments around the earlier call to fixup_ty
197+
let substs_f = match fixup_substs(fcx, sp, trait_id,
198+
substs, is_early) {
199+
some(substs) => substs,
200+
none => {
201+
assert is_early;
202+
// Bail out with a bogus answer
203+
return vtable_param(0, 0);
204+
}
205+
};
206+
168207
connect_trait_tps(fcx, sp, substs_f.tps,
169208
trait_tps, im.did);
170209
let subres = lookup_vtables(fcx, sp, im_bs, &substs_f,
171-
false);
210+
false, is_early);
172211
vec::push(found,
173212
vtable_static(im.did, substs_f.tps,
174213
subres));
@@ -181,8 +220,10 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
181220
0u => { /* fallthrough */ }
182221
1u => { return found[0]; }
183222
_ => {
184-
fcx.ccx.tcx.sess.span_err(
185-
sp, ~"multiple applicable methods in scope");
223+
if !is_early {
224+
fcx.ccx.tcx.sess.span_err(
225+
sp, ~"multiple applicable methods in scope");
226+
}
186227
return found[0];
187228
}
188229
}
@@ -195,17 +236,21 @@ fn lookup_vtable(fcx: @fn_ctxt, sp: span, ty: ty::t, trait_ty: ty::t,
195236
ty_to_str(tcx, ty));
196237
}
197238
198-
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
239+
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t, is_early: bool)
240+
-> option<ty::t> {
199241
let tcx = fcx.ccx.tcx;
200242
match resolve_type(fcx.infcx, ty, resolve_all | force_all) {
201-
result::ok(new_type) => new_type,
202-
result::err(e) => {
243+
result::ok(new_type) => some(new_type),
244+
result::err(e) if !is_early => {
203245
tcx.sess.span_fatal(
204246
sp,
205247
fmt!{"cannot determine a type \
206248
for this bounded type parameter: %s",
207249
fixup_err_to_str(e)})
208250
}
251+
result::err(e) => {
252+
none
253+
}
209254
}
210255
}
211256

@@ -226,7 +271,7 @@ fn connect_trait_tps(fcx: @fn_ctxt, sp: span, impl_tys: ~[ty::t],
226271
}
227272
}
228273

229-
fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
274+
fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
230275
let cx = fcx.ccx;
231276
match ex.node {
232277
ast::expr_path(*) => {
@@ -236,11 +281,9 @@ fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
236281
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
237282
let item_ty = ty::lookup_item_type(cx.tcx, did);
238283
if has_trait_bounds(*item_ty.bounds) {
239-
cx.vtable_map.insert(ex.id, lookup_vtables(fcx,
240-
ex.span,
241-
item_ty.bounds,
242-
substs,
243-
false));
284+
let vtbls = lookup_vtables(fcx, ex.span, item_ty.bounds,
285+
substs, false, is_early);
286+
if !is_early { cx.vtable_map.insert(ex.id, vtbls); }
244287
}
245288
}
246289
_ => ()
@@ -260,11 +303,9 @@ fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
260303
_ => ex.callee_id
261304
};
262305
let substs = fcx.node_ty_substs(callee_id);
263-
cx.vtable_map.insert(callee_id, lookup_vtables(fcx,
264-
ex.span,
265-
bounds,
266-
&substs,
267-
false));
306+
let vtbls = lookup_vtables(fcx, ex.span, bounds,
307+
&substs, false, is_early);
308+
if !is_early { cx.vtable_map.insert(callee_id, vtbls); }
268309
}
269310
}
270311
_ => ()
@@ -280,18 +321,22 @@ fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
280321
passing in the source and target type
281322
*/
282323
let vtable = lookup_vtable(fcx, ex.span, fcx.expr_ty(src),
283-
target_ty, true);
324+
target_ty, true, is_early);
284325
/*
285326
Map this expression to that vtable (that is: "ex has
286327
vtable <vtable>")
287328
*/
288-
cx.vtable_map.insert(ex.id, @~[vtable]);
329+
if !is_early { cx.vtable_map.insert(ex.id, @~[vtable]); }
289330
}
290331
_ => ()
291332
}
292333
}
293334
_ => ()
294335
}
336+
}
337+
338+
fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
339+
early_resolve_expr(ex, fcx, false);
295340
visit::visit_expr(ex, fcx, v);
296341
}
297342

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
trait thing<A> {
3+
fn foo() -> option<A>;
4+
}
5+
impl<A> int: thing<A> {
6+
fn foo() -> option<A> { none }
7+
}
8+
fn foo_func<A, B: thing<A>>(x: B) -> option<A> { x.foo() }
9+
10+
fn main() {
11+
12+
for iter::eachi(some({a: 0})) |i, a| {
13+
#debug["%u %d", i, a.a];
14+
}
15+
16+
let _x: option<float> = foo_func(0);
17+
}

0 commit comments

Comments
 (0)