]> git.lizzy.rs Git - rust.git/commitdiff
refactor trans into trans_closure; restore original closure repr
authorNiko Matsakis <niko@alum.mit.edu>
Wed, 14 Dec 2011 21:57:10 +0000 (13:57 -0800)
committerNiko Matsakis <niko@alum.mit.edu>
Wed, 14 Dec 2011 22:32:28 +0000 (14:32 -0800)
src/comp/back/abi.rs
src/comp/middle/trans.rs
src/comp/middle/trans_closure.rs [new file with mode: 0644]
src/comp/middle/trans_common.rs
src/comp/rustc.rc

index 28a9d239d574417770c9513e8de1cb47bebc2f2c..493c8a04c789dc278d11ca7b061f0bd4f631e63c 100644 (file)
@@ -74,8 +74,8 @@
 
 const closure_elt_tydesc: int = 0;
 const closure_elt_bindings: int = 1;
-const closure_elt_n_ty_params: int = 2;
-const closure_elt_ty_params: int = 3;
+//const closure_elt_n_ty_params: int = 2;
+const closure_elt_ty_params: int = 2; // 3;
 
 const vec_elt_fill: int = 0;
 
index 51194dbc6e5cd3c8ce01428340ca94ae894b064f..ce106c78eace7bc47e8ff7f269d93e62a2f8f7a8 100644 (file)
@@ -1297,7 +1297,7 @@ fn make_take_glue(cx: @block_ctxt, v: ValueRef, t: ty::t) {
 
     fn take_fn_env(cx: @block_ctxt,
                    v: ValueRef,
-                   blk: block(@block_ctxt, ValueRef) -> @block_ctxt)
+                   blk: block(@block_ctxt, ValueRef, ValueRef) -> @block_ctxt)
         -> @block_ctxt {
         let box_cell_v = GEPi(cx, v, [0, abi::fn_field_box]);
         let box_ptr_v = Load(cx, box_cell_v);
@@ -1305,7 +1305,7 @@ fn take_fn_env(cx: @block_ctxt,
         let next_cx = new_sub_block_ctxt(cx, "next");
         let null_test = IsNull(cx, box_ptr_v);
         CondBr(cx, null_test, next_cx.llbb, inner_cx.llbb);
-        inner_cx = blk(inner_cx, box_ptr_v);
+        inner_cx = blk(inner_cx, box_cell_v, box_ptr_v);
         Br(inner_cx, next_cx.llbb);
         ret next_cx;
     }
@@ -1336,7 +1336,7 @@ fn take_fn_env(cx: @block_ctxt,
         bcx
       }
       ty::ty_fn(ast::proto_send., _, _, _, _) {
-        take_fn_env(bcx, v, { |bcx, box_ptr_v|
+        take_fn_env(bcx, v, { |bcx, _box_cell_v, box_ptr_v|
             // Here, box_ptr_v is a unique pointer which
             // must be cloned.
             call_bound_data_glue_for_closure(
@@ -1345,7 +1345,7 @@ fn take_fn_env(cx: @block_ctxt,
         })
       }
       ty::ty_native_fn(_, _) | ty::ty_fn(ast::proto_shared(_), _, _, _, _) {
-        take_fn_env(bcx, v, { |bcx, box_ptr_v|
+        take_fn_env(bcx, v, { |bcx, _box_cell_v, box_ptr_v|
             incr_refcnt_of_boxed(bcx, box_ptr_v)
         })
       }
@@ -2232,42 +2232,6 @@ fn trans_unary(bcx: @block_ctxt, op: ast::unop, e: @ast::expr,
     }
 }
 
-fn trans_expr_fn(bcx: @block_ctxt, f: ast::_fn, sp: span,
-                 id: ast::node_id, dest: dest) -> @block_ctxt {
-    if dest == ignore { ret bcx; }
-    let ccx = bcx_ccx(bcx), bcx = bcx;
-    let fty = node_id_type(ccx, id);
-    check returns_non_ty_var(ccx, fty);
-    let llfnty = type_of_fn_from_ty(ccx, sp, fty, 0u);
-    let sub_cx = extend_path(bcx.fcx.lcx, ccx.names.next("anon"));
-    let s = mangle_internal_name_by_path(ccx, sub_cx.path);
-    let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty);
-
-    let mode = alt f.proto {
-      ast::proto_shared(_) { for_closure }
-      ast::proto_send. { for_send }
-      ast::proto_bare. | ast::proto_block. { for_block }
-    };
-    let env;
-    alt f.proto {
-      ast::proto_block. | ast::proto_shared(_) | ast::proto_send. {
-        let upvars = get_freevars(ccx.tcx, id);
-        let env_r = build_closure(bcx, upvars, mode);
-        env = env_r.ptr;
-        bcx = env_r.bcx;
-        trans_closure(sub_cx, sp, f, llfn, none, [], id, {|fcx|
-            load_environment(bcx, fcx, env_r.ptrty, upvars, mode);
-        });
-      }
-      ast::proto_bare. {
-        env = C_null(T_opaque_closure_ptr(ccx));
-        trans_closure(sub_cx, sp, f, llfn, none, [], id, {|_fcx|});
-      }
-    };
-    fill_fn_pair(bcx, get_dest_addr(dest), llfn, env);
-    ret bcx;
-}
-
 fn trans_compare(cx: @block_ctxt, op: ast::binop, lhs: ValueRef,
                  _lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t) -> result {
     if ty::type_is_scalar(bcx_tcx(cx), rhs_t) {
@@ -2602,294 +2566,6 @@ fn inner(bcx: @block_ctxt, local: @ast::local, curr: ValueRef, t: ty::t,
     ret next_cx;
 }
 
-
-// Iterator translation
-
-tag environment_value {
-    env_expr(@ast::expr);
-    env_direct(ValueRef, ty::t, bool);
-}
-
-// Given a block context and a list of tydescs and values to bind
-// construct a closure out of them. If copying is true, it is a
-// heap allocated closure that copies the upvars into environment.
-// Otherwise, it is stack allocated and copies pointers to the upvars.
-fn build_environment(bcx: @block_ctxt, lltydescs: [ValueRef],
-                     bound_values: [environment_value],
-                     mode: closure_constr_mode) ->
-   {ptr: ValueRef, ptrty: ty::t, bcx: @block_ctxt} {
-
-    fn dummy_environment_box(bcx: @block_ctxt, r: result)
-        -> (@block_ctxt, ValueRef, ValueRef) {
-        // Prevent glue from trying to free this.
-        let ccx = bcx_ccx(bcx);
-        let ref_cnt = GEPi(bcx, r.val, [0, abi::box_rc_field_refcnt]);
-        Store(r.bcx, C_int(ccx, 2), ref_cnt);
-        let closure = GEPi(r.bcx, r.val, [0, abi::box_rc_field_body]);
-        (r.bcx, closure, r.val)
-    }
-
-    fn clone_tydesc(bcx: @block_ctxt,
-                    mode: closure_constr_mode,
-                    td: ValueRef) -> ValueRef {
-        ret alt mode {
-          for_block. | for_closure. { td }
-          for_send. { Call(bcx, bcx_ccx(bcx).upcalls.clone_type_desc, [td]) }
-        };
-    }
-
-    let ccx = bcx_ccx(bcx);
-    let tcx = bcx_tcx(bcx);
-
-    // First, synthesize a tuple type containing the types of all the
-    // bound expressions.
-    // bindings_ty = [bound_ty1, bound_ty2, ...]
-    let bound_tys = [];
-    for bv in bound_values {
-        bound_tys += [alt bv {
-          env_direct(_, t, _) { t }
-          env_expr(e) { ty::expr_ty(tcx, e) }
-        }];
-    }
-    let bindings_ty: ty::t = ty::mk_tup(tcx, bound_tys);
-
-    // NB: keep this in sync with T_closure_ptr; we're making
-    // a ty::t structure that has the same "shape" as the LLVM type
-    // it constructs.
-
-    // Make a vector that contains ty_param_count copies of tydesc_ty.
-    // (We'll need room for that many tydescs in the closure.)
-    let ty_param_count = vec::len(lltydescs);
-    let tydesc_ty: ty::t = ty::mk_type(tcx);
-    let captured_tys: [ty::t] = vec::init_elt(tydesc_ty, ty_param_count);
-
-    // Get all the types we've got (some of which we synthesized
-    // ourselves) into a vector.  The whole things ends up looking
-    // like:
-
-    // closure_ty = (tydesc_ty, (bound_ty1, bound_ty2, ...), int, (tydesc_ty,
-    // tydesc_ty, ...))
-    let closure_tys: [ty::t] =
-        [tydesc_ty, bindings_ty,
-         ty::mk_uint(tcx), ty::mk_tup(tcx, captured_tys)];
-    let closure_ty: ty::t = ty::mk_tup(tcx, closure_tys);
-
-    let temp_cleanups = [];
-
-    // Allocate a box that can hold something closure-sized.
-    //
-    // For now, no matter what kind of closure we have, we always allocate
-    // space for a ref cnt in the closure.  If the closure is a block or
-    // unique closure, this ref count isn't really used: we initialize it to 2
-    // so that it will never drop to zero.  This is a hack and could go away
-    // but then we'd have to modify the code to do the right thing when
-    // casting from a shared closure to a block.
-    let (bcx, closure, box) = alt mode {
-      for_closure. {
-        let r = trans_malloc_boxed(bcx, closure_ty);
-        add_clean_free(bcx, r.box, false);
-        temp_cleanups += [r.box];
-        (r.bcx, r.body, r.box)
-      }
-      for_send. {
-        // Dummy up a box in the exchange heap.
-        let tup_ty = ty::mk_tup(tcx, [ty::mk_int(tcx), closure_ty]);
-        let box_ty = ty::mk_uniq(tcx, {ty: tup_ty, mut: ast::imm});
-        check trans_uniq::type_is_unique_box(bcx, box_ty);
-        let r = trans_uniq::alloc_uniq(bcx, box_ty);
-        add_clean_free(bcx, r.val, true);
-        dummy_environment_box(bcx, r)
-      }
-      for_block. {
-        // Dummy up a box on the stack,
-        let ty = ty::mk_tup(tcx, [ty::mk_int(tcx), closure_ty]);
-        let r = alloc_ty(bcx, ty);
-        dummy_environment_box(bcx, r)
-      }
-    };
-
-    // Store bindings tydesc.
-    alt mode {
-      for_closure. | for_send. {
-        let bound_tydesc = GEPi(bcx, closure, [0, abi::closure_elt_tydesc]);
-        let ti = none;
-        let bindings_tydesc =
-            get_tydesc(bcx, bindings_ty, true, tps_normal, ti).result;
-        lazily_emit_tydesc_glue(bcx, abi::tydesc_field_drop_glue, ti);
-        lazily_emit_tydesc_glue(bcx, abi::tydesc_field_free_glue, ti);
-        bcx = bindings_tydesc.bcx;
-        let td = clone_tydesc(bcx, mode, bindings_tydesc.val);
-        Store(bcx, td, bound_tydesc);
-      }
-      for_block. {}
-    }
-
-    // Copy expr values into boxed bindings.
-    // Silly check
-    check type_is_tup_like(bcx, closure_ty);
-    let closure_box = box;
-    let closure_box_ty = ty::mk_imm_box(bcx_tcx(bcx), closure_ty);
-    let i = 0u;
-    for bv in bound_values {
-        let bound = GEP_tup_like_1(bcx, closure_box_ty, closure_box,
-                                   [0, abi::box_rc_field_body,
-                                    abi::closure_elt_bindings,
-                                    i as int]);
-        bcx = bound.bcx;
-        alt bv {
-          env_expr(e) {
-            bcx = trans_expr_save_in(bcx, e, bound.val);
-            add_clean_temp_mem(bcx, bound.val, bound_tys[i]);
-            temp_cleanups += [bound.val];
-          }
-          env_direct(val, ty, is_mem) {
-            alt mode {
-              for_closure. | for_send. {
-                let val1 = is_mem ? load_if_immediate(bcx, val, ty) : val;
-                bcx = copy_val(bcx, INIT, bound.val, val1, ty);
-              }
-              for_block. {
-                let addr = is_mem ? val : do_spill_noroot(bcx, val);
-                Store(bcx, addr, bound.val);
-              }
-            }
-          }
-        }
-        i += 1u;
-    }
-    for cleanup in temp_cleanups { revoke_clean(bcx, cleanup); }
-
-    // If necessary, copy tydescs describing type parameters into the
-    // appropriate slot in the closure.
-    // Silly check as well
-    check type_is_tup_like(bcx, closure_ty);
-    let {bcx:bcx, val:n_ty_params_slot} =
-        GEP_tup_like(bcx, closure_ty, closure,
-                     [0, abi::closure_elt_n_ty_params]);
-    Store(bcx, C_uint(ccx, vec::len(lltydescs)), n_ty_params_slot);
-    check type_is_tup_like(bcx, closure_ty);
-    let {bcx:bcx, val:ty_params_slot} =
-        GEP_tup_like(bcx, closure_ty, closure,
-                     [0, abi::closure_elt_ty_params]);
-    i = 0u;
-    for td: ValueRef in lltydescs {
-        let ty_param_slot = GEPi(bcx, ty_params_slot, [0, i as int]);
-        let cloned_td = clone_tydesc(bcx, mode, td);
-        Store(bcx, cloned_td, ty_param_slot);
-        i += 1u;
-    }
-
-    ret {ptr: box, ptrty: closure_ty, bcx: bcx};
-}
-
-tag closure_constr_mode {
-    for_block;
-    for_closure;
-    for_send;
-}
-
-// Given a context and a list of upvars, build a closure. This just
-// collects the upvars and packages them up for build_environment.
-fn build_closure(cx: @block_ctxt,
-                 upvars: @[ast::def],
-                 mode: closure_constr_mode)
-    -> {ptr: ValueRef, ptrty: ty::t, bcx: @block_ctxt} {
-    // If we need to, package up the iterator body to call
-    let env_vals = [];
-    // Package up the upvars
-    for def in *upvars {
-        let lv = trans_local_var(cx, def);
-        let nid = ast_util::def_id_of_def(def).node;
-        let ty = ty::node_id_to_monotype(bcx_tcx(cx), nid);
-        alt mode {
-          for_block. { ty = ty::mk_mut_ptr(bcx_tcx(cx), ty); }
-          for_send. | for_closure. {}
-        }
-        env_vals += [env_direct(lv.val, ty, lv.kind == owned)];
-    }
-    ret build_environment(cx, copy cx.fcx.lltydescs, env_vals, mode);
-}
-
-// Return a pointer to the stored typarams in a closure.
-// This is awful. Since the size of the bindings stored in the closure might
-// be dynamically sized, we can't skip past them to get to the tydescs until
-// we have loaded the tydescs. Thus we use the stored size of the bindings
-// in the tydesc for the closure to skip over them. Ugh.
-fn find_environment_tydescs(bcx: @block_ctxt, envty: ty::t, closure: ValueRef)
-   -> ValueRef {
-    ret if !ty::type_has_dynamic_size(bcx_tcx(bcx), envty) {
-
-            // If we can find the typarams statically, do it
-            GEPi(bcx, closure,
-                 [0, abi::box_rc_field_body, abi::closure_elt_ty_params])
-        } else {
-            // Ugh. We need to load the size of the bindings out of the
-            // closure's tydesc and use that to skip over the bindings.
-            let descsty =
-                ty::get_element_type(bcx_tcx(bcx), envty,
-                                     abi::closure_elt_ty_params as uint);
-            let llenv = GEPi(bcx, closure, [0, abi::box_rc_field_body]);
-            // Load the tydesc and find the size of the body
-            let lldesc =
-                Load(bcx, GEPi(bcx, llenv, [0, abi::closure_elt_tydesc]));
-            let llsz =
-                Load(bcx, GEPi(bcx, lldesc, [0, abi::tydesc_field_size]));
-
-            // Get the bindings pointer and add the size to it
-            let llbinds = GEPi(bcx, llenv, [0, abi::closure_elt_bindings]);
-            bump_ptr(bcx, descsty, llbinds, llsz)
-        }
-}
-
-// Given an enclosing block context, a new function context, a closure type,
-// and a list of upvars, generate code to load and populate the environment
-// with the upvars and type descriptors.
-fn load_environment(enclosing_cx: @block_ctxt, fcx: @fn_ctxt, envty: ty::t,
-                    upvars: @[ast::def], mode: closure_constr_mode) {
-    let bcx = new_raw_block_ctxt(fcx, fcx.llloadenv);
-
-    let ty = ty::mk_imm_box(bcx_tcx(bcx), envty);
-
-    let ccx = bcx_ccx(bcx);
-    let sp = bcx.sp;
-    // FIXME: should have postcondition on mk_imm_box,
-    // so this check won't be necessary
-    check (type_has_static_size(ccx, ty));
-    let llty = type_of(ccx, sp, ty);
-    let llclosure = PointerCast(bcx, fcx.llenv, llty);
-
-    // Populate the type parameters from the environment. We need to
-    // do this first because the tydescs are needed to index into
-    // the bindings if they are dynamically sized.
-    let tydesc_count = vec::len(enclosing_cx.fcx.lltydescs);
-    let lltydescs = find_environment_tydescs(bcx, envty, llclosure);
-    let i = 0u;
-    while i < tydesc_count {
-        let lltydescptr = GEPi(bcx, lltydescs, [0, i as int]);
-        fcx.lltydescs += [Load(bcx, lltydescptr)];
-        i += 1u;
-    }
-
-    // Populate the upvars from the environment.
-    let path = [0, abi::box_rc_field_body, abi::closure_elt_bindings];
-    i = 0u;
-    // Load the actual upvars.
-    for upvar_def in *upvars {
-        // Silly check
-        check type_is_tup_like(bcx, ty);
-        let upvarptr = GEP_tup_like(bcx, ty, llclosure, path + [i as int]);
-        bcx = upvarptr.bcx;
-        let llupvarptr = upvarptr.val;
-        alt mode {
-          for_block. { llupvarptr = Load(bcx, llupvarptr); }
-          for_send. | for_closure. { }
-        }
-        let def_id = ast_util::def_id_of_def(upvar_def);
-        fcx.llupvars.insert(def_id.node, llupvarptr);
-        i += 1u;
-    }
-}
-
 fn trans_while(cx: @block_ctxt, cond: @ast::expr, body: ast::blk)
     -> @block_ctxt {
     let next_cx = new_sub_block_ctxt(cx, "while next");
@@ -3292,8 +2968,8 @@ fn lval_maybe_callee_to_lval(c: lval_maybe_callee, ty: ty::t) -> lval_result {
         let n_args = vec::len(ty::ty_fn_args(bcx_tcx(c.bcx), ty));
         let args = vec::init_elt(none::<@ast::expr>, n_args);
         let space = alloc_ty(c.bcx, ty);
-        let bcx = trans_bind_1(space.bcx, ty, c, args, ty,
-                               save_in(space.val));
+        let bcx = trans_closure::trans_bind_1(space.bcx, ty, c, args, ty,
+                                              save_in(space.val));
         add_clean_temp(bcx, space.val, ty);
         ret {bcx: bcx, val: space.val, kind: temporary};
       }
@@ -3580,86 +3256,6 @@ fn trans_bind_thunk(cx: @local_ctxt, sp: span, incoming_fty: ty::t,
     ret {val: llthunk, ty: llthunk_ty};
 }
 
-fn trans_bind(cx: @block_ctxt, f: @ast::expr, args: [option::t<@ast::expr>],
-              id: ast::node_id, dest: dest) -> @block_ctxt {
-    let f_res = trans_callee(cx, f);
-    ret trans_bind_1(cx, ty::expr_ty(bcx_tcx(cx), f), f_res, args,
-                     ty::node_id_to_type(bcx_tcx(cx), id), dest);
-}
-
-fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
-                f_res: lval_maybe_callee,
-                args: [option::t<@ast::expr>], pair_ty: ty::t,
-                dest: dest) -> @block_ctxt {
-    let bound: [@ast::expr] = [];
-    for argopt: option::t<@ast::expr> in args {
-        alt argopt { none. { } some(e) { bound += [e]; } }
-    }
-    let bcx = f_res.bcx;
-    if dest == ignore {
-        for ex in bound { bcx = trans_expr(bcx, ex, ignore); }
-        ret bcx;
-    }
-
-    // Figure out which tydescs we need to pass, if any.
-    let outgoing_fty_real; // the type with typarams still in it
-    let lltydescs: [ValueRef];
-    alt f_res.generic {
-      none. { outgoing_fty_real = outgoing_fty; lltydescs = []; }
-      some(ginfo) {
-        lazily_emit_all_generic_info_tydesc_glues(cx, ginfo);
-        outgoing_fty_real = ginfo.item_type;
-        lltydescs = ginfo.tydescs;
-      }
-    }
-
-    let ty_param_count = vec::len(lltydescs);
-    if vec::len(bound) == 0u && ty_param_count == 0u {
-        // Trivial 'binding': just return the closure
-        let lv = lval_maybe_callee_to_lval(f_res, pair_ty);
-        bcx = lv.bcx;
-        ret memmove_ty(bcx, get_dest_addr(dest), lv.val, pair_ty);
-    }
-    let closure = alt f_res.env {
-      null_env. { none }
-      _ { let (_, cl) = maybe_add_env(cx, f_res); some(cl) }
-    };
-
-    // FIXME: should follow from a precondition on trans_bind_1
-    let ccx = bcx_ccx(cx);
-    check (type_has_static_size(ccx, outgoing_fty));
-
-    // Arrange for the bound function to live in the first binding spot
-    // if the function is not statically known.
-    let (env_vals, target_res) = alt closure {
-      some(cl) {
-        // 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 sp = cx.sp;
-        let llclosurety = T_ptr(type_of(ccx, sp, outgoing_fty));
-        let src_loc = PointerCast(bcx, cl, llclosurety);
-        ([env_direct(src_loc, pair_ty, true)], none)
-      }
-      none. { ([], some(f_res.val)) }
-    };
-
-    // Actually construct the closure
-    let closure = build_environment(bcx, lltydescs, env_vals +
-                                    vec::map({|x| env_expr(x)}, bound),
-                                    for_closure);
-    bcx = closure.bcx;
-
-    // Make thunk
-    let llthunk =
-        trans_bind_thunk(cx.fcx.lcx, cx.sp, pair_ty, outgoing_fty_real, args,
-                         closure.ptrty, ty_param_count, target_res);
-
-    // Fill the function pair
-    fill_fn_pair(bcx, get_dest_addr(dest), llthunk.val, closure.ptr);
-    ret bcx;
-}
-
 fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty0: TypeRef,
                   &to_zero: [{v: ValueRef, t: ty::t}],
                   &to_revoke: [{v: ValueRef, t: ty::t}], e: @ast::expr) ->
@@ -4166,8 +3762,12 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
         ret trans_unary(bcx, op, x, e.id, dest);
       }
       // NDM captures
-      ast::expr_fn(f, cap) { ret trans_expr_fn(bcx, f, e.span, e.id, dest); }
-      ast::expr_bind(f, args) { ret trans_bind(bcx, f, args, e.id, dest); }
+      ast::expr_fn(f, cap) {
+        ret trans_closure::trans_expr_fn(bcx, f, e.span, e.id, dest);
+      }
+      ast::expr_bind(f, args) {
+        ret trans_closure::trans_bind(bcx, f, args, e.id, dest);
+      }
       ast::expr_copy(a) {
         if !ty::expr_is_lval(tcx, a) { ret trans_expr(bcx, a, dest); }
         else { ret lval_to_dps(bcx, a, dest); }
diff --git a/src/comp/middle/trans_closure.rs b/src/comp/middle/trans_closure.rs
new file mode 100644 (file)
index 0000000..852ee5e
--- /dev/null
@@ -0,0 +1,426 @@
+import syntax::ast;
+import syntax::ast_util;
+import lib::llvm::llvm::ValueRef;
+import trans_common::*;
+import trans_build::*;
+import trans::*;
+import middle::freevars::get_freevars;
+import option::{some, none};
+import back::abi;
+import syntax::codemap::span;
+import back::link::mangle_internal_name_by_path;
+import trans::{
+    trans_shared_malloc,
+    type_of_inner,
+    size_of,
+    node_id_type,
+    INIT,
+    trans_shared_free,
+    drop_ty,
+    new_sub_block_ctxt,
+    load_if_immediate,
+    dest
+};
+
+tag environment_value {
+    env_expr(@ast::expr);
+    env_direct(ValueRef, ty::t, bool);
+}
+
+// Given a block context and a list of tydescs and values to bind
+// construct a closure out of them. If copying is true, it is a
+// heap allocated closure that copies the upvars into environment.
+// Otherwise, it is stack allocated and copies pointers to the upvars.
+fn build_environment(bcx: @block_ctxt, lltydescs: [ValueRef],
+                     bound_values: [environment_value],
+                     mode: closure_constr_mode) ->
+   {ptr: ValueRef, ptrty: ty::t, bcx: @block_ctxt} {
+
+    fn dummy_environment_box(bcx: @block_ctxt, r: result)
+        -> (@block_ctxt, ValueRef, ValueRef) {
+        // Prevent glue from trying to free this.
+        let ccx = bcx_ccx(bcx);
+        let ref_cnt = GEPi(bcx, r.val, [0, abi::box_rc_field_refcnt]);
+        Store(r.bcx, C_int(ccx, 2), ref_cnt);
+        let closure = GEPi(r.bcx, r.val, [0, abi::box_rc_field_body]);
+        (r.bcx, closure, r.val)
+    }
+
+    fn clone_tydesc(bcx: @block_ctxt,
+                    mode: closure_constr_mode,
+                    td: ValueRef) -> ValueRef {
+        ret alt mode {
+          for_block. | for_closure. { td }
+          for_send. { Call(bcx, bcx_ccx(bcx).upcalls.clone_type_desc, [td]) }
+        };
+    }
+
+    let ccx = bcx_ccx(bcx);
+    let tcx = bcx_tcx(bcx);
+
+    // First, synthesize a tuple type containing the types of all the
+    // bound expressions.
+    // bindings_ty = [bound_ty1, bound_ty2, ...]
+    let bound_tys = [];
+    for bv in bound_values {
+        bound_tys += [alt bv {
+          env_direct(_, t, _) { t }
+          env_expr(e) { ty::expr_ty(tcx, e) }
+        }];
+    }
+    let bindings_ty: ty::t = ty::mk_tup(tcx, bound_tys);
+
+    // NB: keep this in sync with T_closure_ptr; we're making
+    // a ty::t structure that has the same "shape" as the LLVM type
+    // it constructs.
+
+    // Make a vector that contains ty_param_count copies of tydesc_ty.
+    // (We'll need room for that many tydescs in the closure.)
+    let ty_param_count = vec::len(lltydescs);
+    let tydesc_ty: ty::t = ty::mk_type(tcx);
+    let captured_tys: [ty::t] = vec::init_elt(tydesc_ty, ty_param_count);
+
+    // Get all the types we've got (some of which we synthesized
+    // ourselves) into a vector.  The whole things ends up looking
+    // like:
+
+    // closure_ty = (
+    //   tydesc_ty, (bound_ty1, bound_ty2, ...),
+    //   /*int,*/ (tydesc_ty, tydesc_ty, ...))
+    let closure_tys: [ty::t] =
+        [tydesc_ty, bindings_ty,
+         /*ty::mk_uint(tcx),*/ ty::mk_tup(tcx, captured_tys)];
+    let closure_ty: ty::t = ty::mk_tup(tcx, closure_tys);
+
+    let temp_cleanups = [];
+
+    // Allocate a box that can hold something closure-sized.
+    //
+    // For now, no matter what kind of closure we have, we always allocate
+    // space for a ref cnt in the closure.  If the closure is a block or
+    // unique closure, this ref count isn't really used: we initialize it to 2
+    // so that it will never drop to zero.  This is a hack and could go away
+    // but then we'd have to modify the code to do the right thing when
+    // casting from a shared closure to a block.
+    let (bcx, closure, box) = alt mode {
+      for_closure. {
+        let r = trans::trans_malloc_boxed(bcx, closure_ty);
+        add_clean_free(bcx, r.box, false);
+        temp_cleanups += [r.box];
+        (r.bcx, r.body, r.box)
+      }
+      for_send. {
+        // Dummy up a box in the exchange heap.
+        let tup_ty = ty::mk_tup(tcx, [ty::mk_int(tcx), closure_ty]);
+        let box_ty = ty::mk_uniq(tcx, {ty: tup_ty, mut: ast::imm});
+        check trans_uniq::type_is_unique_box(bcx, box_ty);
+        let r = trans_uniq::alloc_uniq(bcx, box_ty);
+        add_clean_free(bcx, r.val, true);
+        dummy_environment_box(bcx, r)
+      }
+      for_block. {
+        // Dummy up a box on the stack,
+        let ty = ty::mk_tup(tcx, [ty::mk_int(tcx), closure_ty]);
+        let r = trans::alloc_ty(bcx, ty);
+        dummy_environment_box(bcx, r)
+      }
+    };
+
+    // Store bindings tydesc.
+    alt mode {
+      for_closure. | for_send. {
+        let bound_tydesc = GEPi(bcx, closure, [0, abi::closure_elt_tydesc]);
+        let ti = none;
+        let {result:bindings_tydesc, _} =
+            trans::get_tydesc(bcx, bindings_ty, true, trans::tps_normal, ti);
+        trans::lazily_emit_tydesc_glue(bcx, abi::tydesc_field_drop_glue, ti);
+        trans::lazily_emit_tydesc_glue(bcx, abi::tydesc_field_free_glue, ti);
+        bcx = bindings_tydesc.bcx;
+        let td = clone_tydesc(bcx, mode, bindings_tydesc.val);
+        Store(bcx, td, bound_tydesc);
+      }
+      for_block. {}
+    }
+
+    // Copy expr values into boxed bindings.
+    // Silly check
+    check type_is_tup_like(bcx, closure_ty);
+    let closure_box = box;
+    let closure_box_ty = ty::mk_imm_box(bcx_tcx(bcx), closure_ty);
+    let i = 0u;
+    for bv in bound_values {
+        let bound = trans::GEP_tup_like_1(bcx, closure_box_ty, closure_box,
+                                          [0, abi::box_rc_field_body,
+                                           abi::closure_elt_bindings,
+                                           i as int]);
+        bcx = bound.bcx;
+        alt bv {
+          env_expr(e) {
+            bcx = trans::trans_expr_save_in(bcx, e, bound.val);
+            add_clean_temp_mem(bcx, bound.val, bound_tys[i]);
+            temp_cleanups += [bound.val];
+          }
+          env_direct(val, ty, is_mem) {
+            alt mode {
+              for_closure. | for_send. {
+                let val1 = is_mem ? load_if_immediate(bcx, val, ty) : val;
+                bcx = trans::copy_val(bcx, INIT, bound.val, val1, ty);
+              }
+              for_block. {
+                let addr = is_mem ? val : do_spill_noroot(bcx, val);
+                Store(bcx, addr, bound.val);
+              }
+            }
+          }
+        }
+        i += 1u;
+    }
+    for cleanup in temp_cleanups { revoke_clean(bcx, cleanup); }
+
+    // If necessary, copy tydescs describing type parameters into the
+    // appropriate slot in the closure.
+    // Silly check as well
+    //check type_is_tup_like(bcx, closure_ty);
+    //let {bcx:bcx, val:n_ty_params_slot} =
+    //    GEP_tup_like(bcx, closure_ty, closure,
+    //                 [0, abi::closure_elt_n_ty_params]);
+    //Store(bcx, C_uint(ccx, vec::len(lltydescs)), n_ty_params_slot);
+    check type_is_tup_like(bcx, closure_ty);
+    let {bcx:bcx, val:ty_params_slot} =
+        GEP_tup_like(bcx, closure_ty, closure,
+                     [0, abi::closure_elt_ty_params]);
+    i = 0u;
+    for td: ValueRef in lltydescs {
+        let ty_param_slot = GEPi(bcx, ty_params_slot, [0, i as int]);
+        let cloned_td = clone_tydesc(bcx, mode, td);
+        Store(bcx, cloned_td, ty_param_slot);
+        i += 1u;
+    }
+
+    ret {ptr: box, ptrty: closure_ty, bcx: bcx};
+}
+
+tag closure_constr_mode {
+    for_block;
+    for_closure;
+    for_send;
+}
+
+// Given a context and a list of upvars, build a closure. This just
+// collects the upvars and packages them up for build_environment.
+fn build_closure(cx: @block_ctxt,
+                 upvars: @[ast::def],
+                 mode: closure_constr_mode)
+    -> {ptr: ValueRef, ptrty: ty::t, bcx: @block_ctxt} {
+    // If we need to, package up the iterator body to call
+    let env_vals = [];
+    // Package up the upvars
+    for def in *upvars {
+        let lv = trans_local_var(cx, def);
+        let nid = ast_util::def_id_of_def(def).node;
+        let ty = ty::node_id_to_monotype(bcx_tcx(cx), nid);
+        alt mode {
+          for_block. { ty = ty::mk_mut_ptr(bcx_tcx(cx), ty); }
+          for_send. | for_closure. {}
+        }
+        env_vals += [env_direct(lv.val, ty, lv.kind == owned)];
+    }
+    ret build_environment(cx, copy cx.fcx.lltydescs, env_vals, mode);
+}
+
+// Return a pointer to the stored typarams in a closure.
+// This is awful. Since the size of the bindings stored in the closure might
+// be dynamically sized, we can't skip past them to get to the tydescs until
+// we have loaded the tydescs. Thus we use the stored size of the bindings
+// in the tydesc for the closure to skip over them. Ugh.
+fn find_environment_tydescs(bcx: @block_ctxt, envty: ty::t, closure: ValueRef)
+   -> ValueRef {
+    ret if !ty::type_has_dynamic_size(bcx_tcx(bcx), envty) {
+
+            // If we can find the typarams statically, do it
+            GEPi(bcx, closure,
+                 [0, abi::box_rc_field_body, abi::closure_elt_ty_params])
+        } else {
+            // Ugh. We need to load the size of the bindings out of the
+            // closure's tydesc and use that to skip over the bindings.
+            let descsty =
+                ty::get_element_type(bcx_tcx(bcx), envty,
+                                     abi::closure_elt_ty_params as uint);
+            let llenv = GEPi(bcx, closure, [0, abi::box_rc_field_body]);
+            // Load the tydesc and find the size of the body
+            let lldesc =
+                Load(bcx, GEPi(bcx, llenv, [0, abi::closure_elt_tydesc]));
+            let llsz =
+                Load(bcx, GEPi(bcx, lldesc, [0, abi::tydesc_field_size]));
+
+            // Get the bindings pointer and add the size to it
+            let llbinds = GEPi(bcx, llenv, [0, abi::closure_elt_bindings]);
+            bump_ptr(bcx, descsty, llbinds, llsz)
+        }
+}
+
+// Given an enclosing block context, a new function context, a closure type,
+// and a list of upvars, generate code to load and populate the environment
+// with the upvars and type descriptors.
+fn load_environment(enclosing_cx: @block_ctxt, fcx: @fn_ctxt, envty: ty::t,
+                    upvars: @[ast::def], mode: closure_constr_mode) {
+    let bcx = new_raw_block_ctxt(fcx, fcx.llloadenv);
+
+    let ty = ty::mk_imm_box(bcx_tcx(bcx), envty);
+
+    let ccx = bcx_ccx(bcx);
+    let sp = bcx.sp;
+    // FIXME: should have postcondition on mk_imm_box,
+    // so this check won't be necessary
+    check (type_has_static_size(ccx, ty));
+    let llty = type_of(ccx, sp, ty);
+    let llclosure = PointerCast(bcx, fcx.llenv, llty);
+
+    // Populate the type parameters from the environment. We need to
+    // do this first because the tydescs are needed to index into
+    // the bindings if they are dynamically sized.
+    let tydesc_count = vec::len(enclosing_cx.fcx.lltydescs);
+    let lltydescs = find_environment_tydescs(bcx, envty, llclosure);
+    let i = 0u;
+    while i < tydesc_count {
+        let lltydescptr = GEPi(bcx, lltydescs, [0, i as int]);
+        fcx.lltydescs += [Load(bcx, lltydescptr)];
+        i += 1u;
+    }
+
+    // Populate the upvars from the environment.
+    let path = [0, abi::box_rc_field_body, abi::closure_elt_bindings];
+    i = 0u;
+    // Load the actual upvars.
+    for upvar_def in *upvars {
+        // Silly check
+        check type_is_tup_like(bcx, ty);
+        let upvarptr = GEP_tup_like(bcx, ty, llclosure, path + [i as int]);
+        bcx = upvarptr.bcx;
+        let llupvarptr = upvarptr.val;
+        alt mode {
+          for_block. { llupvarptr = Load(bcx, llupvarptr); }
+          for_send. | for_closure. { }
+        }
+        let def_id = ast_util::def_id_of_def(upvar_def);
+        fcx.llupvars.insert(def_id.node, llupvarptr);
+        i += 1u;
+    }
+}
+
+fn trans_expr_fn(bcx: @block_ctxt, f: ast::_fn, sp: span,
+                 id: ast::node_id, dest: dest) -> @block_ctxt {
+    if dest == ignore { ret bcx; }
+    let ccx = bcx_ccx(bcx), bcx = bcx;
+    let fty = node_id_type(ccx, id);
+    check returns_non_ty_var(ccx, fty);
+    let llfnty = type_of_fn_from_ty(ccx, sp, fty, 0u);
+    let sub_cx = extend_path(bcx.fcx.lcx, ccx.names.next("anon"));
+    let s = mangle_internal_name_by_path(ccx, sub_cx.path);
+    let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty);
+
+    let mode = alt f.proto {
+      ast::proto_shared(_) { for_closure }
+      ast::proto_send. { for_send }
+      ast::proto_bare. | ast::proto_block. { for_block }
+    };
+    let env;
+    alt f.proto {
+      ast::proto_block. | ast::proto_shared(_) | ast::proto_send. {
+        let upvars = get_freevars(ccx.tcx, id);
+        let env_r = build_closure(bcx, upvars, mode);
+        env = env_r.ptr;
+        bcx = env_r.bcx;
+        trans_closure(sub_cx, sp, f, llfn, none, [], id, {|fcx|
+            load_environment(bcx, fcx, env_r.ptrty, upvars, mode);
+        });
+      }
+      ast::proto_bare. {
+        env = C_null(T_opaque_closure_ptr(ccx));
+        trans_closure(sub_cx, sp, f, llfn, none, [], id, {|_fcx|});
+      }
+    };
+    fill_fn_pair(bcx, get_dest_addr(dest), llfn, env);
+    ret bcx;
+}
+
+fn trans_bind(cx: @block_ctxt, f: @ast::expr, args: [option::t<@ast::expr>],
+              id: ast::node_id, dest: dest) -> @block_ctxt {
+    let f_res = trans_callee(cx, f);
+    ret trans_bind_1(cx, ty::expr_ty(bcx_tcx(cx), f), f_res, args,
+                     ty::node_id_to_type(bcx_tcx(cx), id), dest);
+}
+
+fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
+                f_res: lval_maybe_callee,
+                args: [option::t<@ast::expr>], pair_ty: ty::t,
+                dest: dest) -> @block_ctxt {
+    let bound: [@ast::expr] = [];
+    for argopt: option::t<@ast::expr> in args {
+        alt argopt { none. { } some(e) { bound += [e]; } }
+    }
+    let bcx = f_res.bcx;
+    if dest == ignore {
+        for ex in bound { bcx = trans_expr(bcx, ex, ignore); }
+        ret bcx;
+    }
+
+    // Figure out which tydescs we need to pass, if any.
+    let outgoing_fty_real; // the type with typarams still in it
+    let lltydescs: [ValueRef];
+    alt f_res.generic {
+      none. { outgoing_fty_real = outgoing_fty; lltydescs = []; }
+      some(ginfo) {
+        lazily_emit_all_generic_info_tydesc_glues(cx, ginfo);
+        outgoing_fty_real = ginfo.item_type;
+        lltydescs = ginfo.tydescs;
+      }
+    }
+
+    let ty_param_count = vec::len(lltydescs);
+    if vec::len(bound) == 0u && ty_param_count == 0u {
+        // Trivial 'binding': just return the closure
+        let lv = lval_maybe_callee_to_lval(f_res, pair_ty);
+        bcx = lv.bcx;
+        ret memmove_ty(bcx, get_dest_addr(dest), lv.val, pair_ty);
+    }
+    let closure = alt f_res.env {
+      null_env. { none }
+      _ { let (_, cl) = maybe_add_env(cx, f_res); some(cl) }
+    };
+
+    // FIXME: should follow from a precondition on trans_bind_1
+    let ccx = bcx_ccx(cx);
+    check (type_has_static_size(ccx, outgoing_fty));
+
+    // Arrange for the bound function to live in the first binding spot
+    // if the function is not statically known.
+    let (env_vals, target_res) = alt closure {
+      some(cl) {
+        // 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 sp = cx.sp;
+        let llclosurety = T_ptr(type_of(ccx, sp, outgoing_fty));
+        let src_loc = PointerCast(bcx, cl, llclosurety);
+        ([env_direct(src_loc, pair_ty, true)], none)
+      }
+      none. { ([], some(f_res.val)) }
+    };
+
+    // Actually construct the closure
+    let closure = build_environment(bcx, lltydescs, env_vals +
+                                    vec::map({|x| env_expr(x)}, bound),
+                                    for_closure);
+    bcx = closure.bcx;
+
+    // Make thunk
+    let llthunk =
+        trans_bind_thunk(cx.fcx.lcx, cx.sp, pair_ty, outgoing_fty_real, args,
+                         closure.ptrty, ty_param_count, target_res);
+
+    // Fill the function pair
+    fill_fn_pair(bcx, get_dest_addr(dest), llthunk.val, closure.ptr);
+    ret bcx;
+}
+
index 1bf3bd38931ee89dbc63fba73706ae18debd45a1..ff6de79399969c460eac093584d9c2e4d92736a3 100644 (file)
@@ -695,7 +695,7 @@ fn T_closure_ptr(cx: @crate_ctxt, llbindings_ty: TypeRef,
     // it constructs.
     ret T_ptr(T_box(cx, T_struct([T_ptr(cx.tydesc_type),
                                   llbindings_ty,
-                                  cx.int_type,
+                                  //cx.int_type,
                                   T_captured_tydescs(cx, n_ty_params)])));
 }
 
index 14b68ae6deefb442c885101f5b19f94234ea5236..51a01378fb3a9c95373fb165610af06a4ab20a9a 100644 (file)
@@ -20,6 +20,7 @@ mod middle {
     mod trans_alt;
     mod trans_objects;
     mod trans_uniq;
+    mod trans_closure;
     mod trans_vec;
     mod ty;
     mod ast_map;