Get rid of a lot of bind related cruft as part of Issue #2189.
This commit is contained in:
parent
40fc1737b2
commit
95dd9f3204
@ -2660,7 +2660,7 @@ fn trans_lval(cx: block, e: @ast::expr) -> lval_result {
|
||||
alt e.node {
|
||||
ast::expr_path(_) {
|
||||
let v = trans_path(cx, e.id);
|
||||
ret lval_maybe_callee_to_lval(v, expr_ty(cx, e));
|
||||
ret lval_maybe_callee_to_lval(v, e.span);
|
||||
}
|
||||
ast::expr_field(base, ident, _) {
|
||||
ret trans_rec_field(cx, base, ident);
|
||||
@ -2711,26 +2711,18 @@ fn non_gc_box_cast(cx: block, val: ValueRef) -> ValueRef {
|
||||
PointerCast(cx, val, non_gc_t)
|
||||
}
|
||||
|
||||
fn lval_maybe_callee_to_lval(c: lval_maybe_callee, ty: ty::t) -> lval_result {
|
||||
let must_bind = alt c.env { self_env(_, _, _) { true } _ { false } };
|
||||
if must_bind {
|
||||
let n_args = ty::ty_fn_args(ty).len();
|
||||
let args = vec::from_elem(n_args, none);
|
||||
let space = alloc_ty(c.bcx, ty);
|
||||
let bcx = closure::trans_bind_1(c.bcx, ty, c, args, ty,
|
||||
save_in(space));
|
||||
add_clean_temp(bcx, space, ty);
|
||||
{bcx: bcx, val: space, kind: temporary}
|
||||
} else {
|
||||
alt check c.env {
|
||||
is_closure { {bcx: c.bcx, val: c.val, kind: c.kind} }
|
||||
null_env {
|
||||
let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
|
||||
let llfn = create_real_fn_pair(c.bcx, llfnty, c.val,
|
||||
null_env_ptr(c.bcx));
|
||||
{bcx: c.bcx, val: llfn, kind: temporary}
|
||||
}
|
||||
}
|
||||
fn lval_maybe_callee_to_lval(c: lval_maybe_callee, sp: span) -> lval_result {
|
||||
alt c.env {
|
||||
self_env(*) {
|
||||
c.bcx.sess().span_bug(sp, "implicitly binding method call");
|
||||
}
|
||||
is_closure { {bcx: c.bcx, val: c.val, kind: c.kind} }
|
||||
null_env {
|
||||
let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
|
||||
let llfn = create_real_fn_pair(c.bcx, llfnty, c.val,
|
||||
null_env_ptr(c.bcx));
|
||||
{bcx: c.bcx, val: llfn, kind: temporary}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3605,7 +3597,7 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
|
||||
ast::expr_field(base, _, _) {
|
||||
if dest == ignore { ret trans_expr(bcx, base, ignore); }
|
||||
let callee = trans_callee(bcx, e), ty = expr_ty(bcx, e);
|
||||
let lv = lval_maybe_callee_to_lval(callee, ty);
|
||||
let lv = lval_maybe_callee_to_lval(callee, e.span);
|
||||
revoke_clean(lv.bcx, lv.val);
|
||||
memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, ty);
|
||||
ret lv.bcx;
|
||||
|
@ -90,9 +90,6 @@ import dvec::extensions;
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
enum environment_value {
|
||||
// Evaluate expr and store result in env (used for bind).
|
||||
env_expr(@ast::expr, ty::t),
|
||||
|
||||
// Copy the value from this llvm ValueRef into the environment.
|
||||
env_copy(ValueRef, ty::t, lval_kind),
|
||||
|
||||
@ -105,7 +102,6 @@ enum environment_value {
|
||||
|
||||
fn ev_to_str(ccx: @crate_ctxt, ev: environment_value) -> str {
|
||||
alt ev {
|
||||
env_expr(ex, _) { expr_to_str(ex) }
|
||||
env_copy(v, t, lk) { #fmt("copy(%s,%s)", val_str(ccx.tn, v),
|
||||
ty_to_str(ccx.tcx, t)) }
|
||||
env_move(v, t, lk) { #fmt("move(%s,%s)", val_str(ccx.tn, v),
|
||||
@ -123,7 +119,7 @@ fn mk_tuplified_uniq_cbox_ty(tcx: ty::ctxt, cdata_ty: ty::t) -> ty::t {
|
||||
// Given a closure ty, emits a corresponding tuple ty
|
||||
fn mk_closure_tys(tcx: ty::ctxt,
|
||||
bound_values: ~[environment_value])
|
||||
-> (ty::t, ~[ty::t]) {
|
||||
-> ty::t {
|
||||
let mut bound_tys = ~[];
|
||||
|
||||
// Compute the closed over data
|
||||
@ -132,7 +128,6 @@ fn mk_closure_tys(tcx: ty::ctxt,
|
||||
env_copy(_, t, _) { t }
|
||||
env_move(_, t, _) { t }
|
||||
env_ref(_, t, _) { t }
|
||||
env_expr(_, t) { t }
|
||||
});
|
||||
}
|
||||
let bound_data_ty = ty::mk_tup(tcx, bound_tys);
|
||||
@ -140,7 +135,7 @@ fn mk_closure_tys(tcx: ty::ctxt,
|
||||
let cdata_ty = ty::mk_tup(tcx, ~[ty::mk_tup(tcx, ~[]),
|
||||
bound_data_ty]);
|
||||
#debug["cdata_ty=%s", ty_to_str(tcx, cdata_ty)];
|
||||
ret (cdata_ty, bound_tys);
|
||||
ret cdata_ty;
|
||||
}
|
||||
|
||||
fn allocate_cbox(bcx: block,
|
||||
@ -196,8 +191,7 @@ fn store_environment(bcx: block,
|
||||
let ccx = bcx.ccx(), tcx = ccx.tcx;
|
||||
|
||||
// compute the shape of the closure
|
||||
let (cdata_ty, bound_tys) =
|
||||
mk_closure_tys(tcx, bound_values);
|
||||
let cdata_ty = mk_closure_tys(tcx, bound_values);
|
||||
|
||||
// allocate closure in the heap
|
||||
let llbox = allocate_cbox(bcx, ck, cdata_ty);
|
||||
@ -225,11 +219,6 @@ fn store_environment(bcx: block,
|
||||
let bound_data = GEPi(bcx, llbox,
|
||||
~[0u, abi::box_field_body, abi::closure_body_bindings, i]);
|
||||
alt bv {
|
||||
env_expr(e, _) {
|
||||
bcx = base::trans_expr_save_in(bcx, e, bound_data);
|
||||
add_clean_temp_mem(bcx, bound_data, bound_tys[i]);
|
||||
vec::push(temp_cleanups, bound_data);
|
||||
}
|
||||
env_copy(val, ty, owned) {
|
||||
let val1 = load_if_immediate(bcx, val, ty);
|
||||
bcx = base::copy_val(bcx, INIT, bound_data, val1, ty);
|
||||
@ -415,70 +404,6 @@ fn trans_expr_fn(bcx: block,
|
||||
ret bcx;
|
||||
}
|
||||
|
||||
fn trans_bind_1(cx: block, outgoing_fty: ty::t,
|
||||
f_res: lval_maybe_callee,
|
||||
args: ~[option<@ast::expr>], pair_ty: ty::t,
|
||||
dest: dest) -> block {
|
||||
let _icx = cx.insn_ctxt("closure::trans_bind1");
|
||||
let ccx = cx.ccx();
|
||||
let mut bound: ~[@ast::expr] = ~[];
|
||||
for vec::each(args) |argopt| {
|
||||
alt argopt { none { } some(e) { vec::push(bound, e); } }
|
||||
}
|
||||
let mut bcx = f_res.bcx;
|
||||
if dest == ignore {
|
||||
for vec::each(bound) |ex| { bcx = trans_expr(bcx, ex, ignore); }
|
||||
ret bcx;
|
||||
}
|
||||
|
||||
if bound.len() == 0u &&
|
||||
(f_res.env == null_env || f_res.env == is_closure) {
|
||||
// Trivial 'binding': just return the closure
|
||||
let lv = lval_maybe_callee_to_lval(f_res, pair_ty);
|
||||
memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, pair_ty);
|
||||
ret lv.bcx;
|
||||
}
|
||||
|
||||
// Arrange for the bound function to live in the first binding spot
|
||||
// if the function is not statically known.
|
||||
let (env_vals, target_info) = alt f_res.env {
|
||||
null_env { (~[], target_static(f_res.val)) }
|
||||
is_closure {
|
||||
// Cast the function we are binding to be the type that the
|
||||
// closure will expect it to have. The type the closure knows
|
||||
// about has the type parameters substituted with the real types.
|
||||
let llclosurety = T_ptr(type_of(ccx, outgoing_fty));
|
||||
let src_loc = PointerCast(bcx, f_res.val, llclosurety);
|
||||
(~[env_copy(src_loc, pair_ty, owned)], target_closure)
|
||||
}
|
||||
self_env(slf, slf_t, none) {
|
||||
(~[env_copy(slf, slf_t, owned)], target_static_self(f_res.val))
|
||||
}
|
||||
self_env(_, slf_t, some(slf)) {
|
||||
let cast = PointerCast(bcx, f_res.val, T_ptr(T_nil()));
|
||||
(~[env_copy(cast, ty::mk_nil_ptr(ccx.tcx), owned_imm),
|
||||
env_copy(slf, slf_t, owned_imm)], target_self)
|
||||
}
|
||||
};
|
||||
|
||||
// Actually construct the closure
|
||||
let {llbox, cdata_ty, bcx} = store_environment(
|
||||
bcx, vec::append(env_vals,
|
||||
vec::map(bound, |x| {
|
||||
env_expr(x, expr_ty(bcx, x))
|
||||
})),
|
||||
ty::ck_box);
|
||||
|
||||
// Make thunk
|
||||
let llthunk = trans_bind_thunk(
|
||||
cx.fcx.ccx, cx.fcx.path, pair_ty, outgoing_fty, args,
|
||||
cdata_ty, target_info);
|
||||
|
||||
// Fill the function pair
|
||||
fill_fn_pair(bcx, get_dest_addr(dest), llthunk.val, llbox);
|
||||
ret bcx;
|
||||
}
|
||||
|
||||
fn make_fn_glue(
|
||||
cx: block,
|
||||
v: ValueRef,
|
||||
@ -611,163 +536,3 @@ fn make_opaque_cbox_free_glue(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum target_info {
|
||||
target_closure,
|
||||
target_static(ValueRef),
|
||||
target_self,
|
||||
target_static_self(ValueRef),
|
||||
}
|
||||
|
||||
// pth is cx.path
|
||||
fn trans_bind_thunk(ccx: @crate_ctxt,
|
||||
path: path,
|
||||
incoming_fty: ty::t,
|
||||
outgoing_fty: ty::t,
|
||||
args: ~[option<@ast::expr>],
|
||||
cdata_ty: ty::t,
|
||||
target_info: target_info)
|
||||
-> {val: ValueRef, ty: TypeRef} {
|
||||
let _icx = ccx.insn_ctxt("closure::trans_bind_thunk");
|
||||
let tcx = ccx.tcx;
|
||||
#debug["trans_bind_thunk[incoming_fty=%s,outgoing_fty=%s,\
|
||||
cdata_ty=%s]/~",
|
||||
ty_to_str(tcx, incoming_fty),
|
||||
ty_to_str(tcx, outgoing_fty),
|
||||
ty_to_str(tcx, cdata_ty)];
|
||||
|
||||
// Here we're not necessarily constructing a thunk in the sense of
|
||||
// "function with no arguments". The result of compiling 'bind f(foo,
|
||||
// bar, baz)' would be a thunk that, when called, applies f to those
|
||||
// arguments and returns the result. But we're stretching the meaning of
|
||||
// the word "thunk" here to also mean the result of compiling, say, 'bind
|
||||
// f(foo, _, baz)', or any other bind expression that binds f and leaves
|
||||
// some (or all) of the arguments unbound.
|
||||
|
||||
// Here, 'incoming_fty' is the type of the entire bind expression, while
|
||||
// 'outgoing_fty' is the type of the function that is having some of its
|
||||
// arguments bound. If f is a function that takes three arguments of type
|
||||
// int and returns int, and we're translating, say, 'bind f(3, _, 5)',
|
||||
// then outgoing_fty is the type of f, which is (int, int, int) -> int,
|
||||
// and incoming_fty is the type of 'bind f(3, _, 5)', which is int -> int.
|
||||
|
||||
// Once translated, the entire bind expression will be the call f(foo,
|
||||
// bar, baz) wrapped in a (so-called) thunk that takes 'bar' as its
|
||||
// argument and that has bindings of 'foo' to 3 and 'baz' to 5 and a
|
||||
// pointer to 'f' all saved in its environment. So, our job is to
|
||||
// construct and return that thunk.
|
||||
|
||||
// Give the thunk a name, type, and value.
|
||||
let s = mangle_internal_name_by_path_and_seq(ccx, path, @"thunk");
|
||||
let llthunk_ty = get_pair_fn_ty(type_of(ccx, incoming_fty));
|
||||
let llthunk = decl_internal_cdecl_fn(ccx.llmod, s, llthunk_ty);
|
||||
|
||||
// Create a new function context and block context for the thunk, and hold
|
||||
// onto a pointer to the first block in the function for later use.
|
||||
let fcx = new_fn_ctxt(ccx, path, llthunk, none);
|
||||
let mut bcx = top_scope_block(fcx, none);
|
||||
let lltop = bcx.llbb;
|
||||
// Since we might need to construct derived tydescs that depend on
|
||||
// our bound tydescs, we need to load tydescs out of the environment
|
||||
// before derived tydescs are constructed. To do this, we load them
|
||||
// in the load_env block.
|
||||
let l_bcx = raw_block(fcx, fcx.llloadenv);
|
||||
|
||||
// The 'llenv' that will arrive in the thunk we're creating is an
|
||||
// environment that will contain the values of its arguments and a
|
||||
// pointer to the original function. This environment is always
|
||||
// stored like an opaque box (see big comment at the header of the
|
||||
// file), so we load the body body, which contains the type descr
|
||||
// and cached data.
|
||||
let llcdata = base::opaque_box_body(l_bcx, cdata_ty, fcx.llenv);
|
||||
|
||||
// "target", in this context, means the function that's having some of its
|
||||
// arguments bound and that will be called inside the thunk we're
|
||||
// creating. (In our running example, target is the function f.) Pick
|
||||
// out the pointer to the target function from the environment. The
|
||||
// target function lives in the first binding spot.
|
||||
let (lltargetfn, lltargetenv, starting_idx) = alt target_info {
|
||||
target_static(fptr) {
|
||||
(fptr, llvm::LLVMGetUndef(T_opaque_cbox_ptr(ccx)), 0u)
|
||||
}
|
||||
target_closure {
|
||||
let pair = GEPi(bcx, llcdata, ~[0u, abi::closure_body_bindings, 0u]);
|
||||
let lltargetenv =
|
||||
Load(bcx, GEPi(bcx, pair, ~[0u, abi::fn_field_box]));
|
||||
let lltargetfn = Load
|
||||
(bcx, GEPi(bcx, pair, ~[0u, abi::fn_field_code]));
|
||||
(lltargetfn, lltargetenv, 1u)
|
||||
}
|
||||
target_self {
|
||||
let fptr = Load(bcx, GEPi(bcx, llcdata,
|
||||
~[0u, abi::closure_body_bindings, 0u]));
|
||||
let slfbox =
|
||||
GEPi(bcx, llcdata, ~[0u, abi::closure_body_bindings, 1u]);
|
||||
let selfptr =
|
||||
GEPi(bcx, Load(bcx, slfbox), ~[0u, abi::box_field_body]);
|
||||
(fptr, PointerCast(bcx, selfptr, T_opaque_cbox_ptr(ccx)), 2u)
|
||||
}
|
||||
target_static_self(fptr) {
|
||||
let slfptr =
|
||||
GEPi(bcx, llcdata, ~[0u, abi::closure_body_bindings, 0u]);
|
||||
(fptr, PointerCast(bcx, slfptr, T_opaque_cbox_ptr(ccx)), 1u)
|
||||
}
|
||||
};
|
||||
|
||||
// And then, pick out the target function's own environment. That's what
|
||||
// we'll use as the environment the thunk gets.
|
||||
|
||||
// Get the types of the arguments to f.
|
||||
let outgoing_args = ty::ty_fn_args(outgoing_fty);
|
||||
|
||||
// Set up the three implicit arguments to the thunk.
|
||||
let mut llargs: ~[ValueRef] = ~[fcx.llretptr, lltargetenv];
|
||||
|
||||
let mut a: uint = first_real_arg; // retptr, env come first
|
||||
let mut b: uint = starting_idx;
|
||||
let mut outgoing_arg_index: uint = 0u;
|
||||
for vec::each(args) |arg| {
|
||||
let out_arg = outgoing_args[outgoing_arg_index];
|
||||
alt arg {
|
||||
// Arg provided at binding time; thunk copies it from
|
||||
// closure.
|
||||
some(e) {
|
||||
let mut val =
|
||||
GEPi(bcx, llcdata, ~[0u, abi::closure_body_bindings, b]);
|
||||
|
||||
alt ty::resolved_mode(tcx, out_arg.mode) {
|
||||
ast::by_val {
|
||||
val = Load(bcx, val);
|
||||
}
|
||||
ast::by_copy {
|
||||
let alloc = alloc_ty(bcx, out_arg.ty);
|
||||
memmove_ty(bcx, alloc, val, out_arg.ty);
|
||||
bcx = take_ty(bcx, alloc, out_arg.ty);
|
||||
val = alloc;
|
||||
}
|
||||
ast::by_ref | ast::by_mutbl_ref | ast::by_move { }
|
||||
}
|
||||
vec::push(llargs, val);
|
||||
b += 1u;
|
||||
}
|
||||
|
||||
// Arg will be provided when the thunk is invoked.
|
||||
none {
|
||||
vec::push(llargs, llvm::LLVMGetParam(llthunk, a as c_uint));
|
||||
a += 1u;
|
||||
}
|
||||
}
|
||||
outgoing_arg_index += 1u;
|
||||
}
|
||||
|
||||
// Cast the outgoing function to the appropriate type.
|
||||
// This is necessary because the type of the function that we have
|
||||
// in the closure does not know how many type descriptors the function
|
||||
// needs to take.
|
||||
let lltargetty = type_of_fn_from_ty(ccx, outgoing_fty);
|
||||
let lltargetfn = PointerCast(bcx, lltargetfn, T_ptr(lltargetty));
|
||||
Call(bcx, lltargetfn, llargs);
|
||||
build_return(bcx);
|
||||
finish_fn(fcx, lltop);
|
||||
ret {val: llthunk, ty: llthunk_ty};
|
||||
}
|
||||
|
@ -710,18 +710,18 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
#debug(">> typechecking expr %d (%s)",
|
||||
expr.id, syntax::print::pprust::expr_to_str(expr));
|
||||
|
||||
// A generic function to factor out common logic from call and bind
|
||||
// expressions.
|
||||
fn check_call_or_bind(
|
||||
// A generic function to factor out common logic from call and
|
||||
// overloaded operations
|
||||
fn check_call_inner(
|
||||
fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, in_fty: ty::t,
|
||||
args: ~[option<@ast::expr>]) -> {fty: ty::t, bot: bool} {
|
||||
args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} {
|
||||
|
||||
let mut bot = false;
|
||||
|
||||
// Replace all region parameters in the arguments and return
|
||||
// type with fresh region variables.
|
||||
|
||||
#debug["check_call_or_bind: before universal quant., in_fty=%s",
|
||||
#debug["check_call_inner: before universal quant., in_fty=%s",
|
||||
fcx.infcx.ty_to_str(in_fty)];
|
||||
|
||||
// This is subtle: we expect `fty` to be a function type, which
|
||||
@ -747,7 +747,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
};
|
||||
|
||||
let fty = ty::mk_fn(fcx.tcx(), fn_ty);
|
||||
#debug["check_call_or_bind: after universal quant., fty=%s",
|
||||
#debug["check_call_inner: after universal quant., fty=%s",
|
||||
fcx.infcx.ty_to_str(fty)];
|
||||
|
||||
let supplied_arg_count = vec::len(args);
|
||||
@ -782,23 +782,18 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
// of arguments when we typecheck the functions. This isn't really the
|
||||
// right way to do this.
|
||||
for [false, true]/_.each |check_blocks| {
|
||||
for args.eachi |i, a_opt| {
|
||||
alt a_opt {
|
||||
some(a) {
|
||||
let is_block = alt a.node {
|
||||
ast::expr_fn_block(*) { true }
|
||||
_ { false }
|
||||
};
|
||||
if is_block == check_blocks {
|
||||
let arg_ty = arg_tys[i];
|
||||
bot |= check_expr_with_unifier(
|
||||
fcx, a, some(arg_ty),
|
||||
|| demand::assign(fcx, a.span, call_expr_id,
|
||||
arg_ty, a)
|
||||
for args.eachi |i, a| {
|
||||
let is_block = alt a.node {
|
||||
ast::expr_fn_block(*) { true }
|
||||
_ { false }
|
||||
};
|
||||
if is_block == check_blocks {
|
||||
let arg_ty = arg_tys[i];
|
||||
bot |= check_expr_with_unifier(
|
||||
fcx, a, some(arg_ty),
|
||||
|| demand::assign(fcx, a.span, call_expr_id,
|
||||
arg_ty, a)
|
||||
);
|
||||
}
|
||||
}
|
||||
none { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -824,9 +819,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
|
||||
// Call the generic checker.
|
||||
let fty = {
|
||||
let args_opt = args.map(|arg| some(arg));
|
||||
let r = check_call_or_bind(fcx, sp, call_expr_id,
|
||||
fn_ty, args_opt);
|
||||
let r = check_call_inner(fcx, sp, call_expr_id,
|
||||
fn_ty, args);
|
||||
bot |= r.bot;
|
||||
r.fty
|
||||
};
|
||||
@ -890,7 +884,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
}
|
||||
fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr,
|
||||
self_ex: @ast::expr, self_t: ty::t,
|
||||
opname: str, args: ~[option<@ast::expr>])
|
||||
opname: str, args: ~[@ast::expr])
|
||||
-> option<(ty::t, bool)> {
|
||||
let callee_id = ast_util::op_expr_callee_id(op_ex);
|
||||
let lkup = method::lookup(fcx, op_ex, self_ex, op_ex.id,
|
||||
@ -899,8 +893,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
some(origin) {
|
||||
let {fty: method_ty, bot: bot} = {
|
||||
let method_ty = fcx.node_ty(callee_id);
|
||||
check_call_or_bind(fcx, op_ex.span, op_ex.id,
|
||||
method_ty, args)
|
||||
check_call_inner(fcx, op_ex.span, op_ex.id,
|
||||
method_ty, args)
|
||||
};
|
||||
fcx.ccx.method_map.insert(op_ex.id, origin);
|
||||
some((ty::ty_fn_ret(method_ty), bot))
|
||||
@ -965,7 +959,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
some(name) {
|
||||
alt lookup_op_method(fcx, ex,
|
||||
lhs_expr, lhs_resolved_t,
|
||||
name, ~[some(rhs)]) {
|
||||
name, ~[rhs]) {
|
||||
some(pair) { ret pair; }
|
||||
_ {}
|
||||
}
|
||||
@ -1589,7 +1583,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
let resolved = structurally_resolved_type(fcx, expr.span,
|
||||
raw_base_t);
|
||||
alt lookup_op_method(fcx, expr, base, resolved, "[]",
|
||||
~[some(idx)]) {
|
||||
~[idx]) {
|
||||
some((ret_ty, _)) { fcx.write_ty(id, ret_ty); }
|
||||
_ {
|
||||
tcx.sess.span_fatal(
|
||||
|
Loading…
x
Reference in New Issue
Block a user