<commit_before>import syntax::ast;
import syntax::ast_util;
import lib::llvm::llvm;
import llvm::{ValueRef, TypeRef};
import trans_common::*;
import trans_build::*;
import trans::*;
import middle::freevars::{get_freevars, freevar_info};
import option::{some, none};
import back::abi;
import syntax::codemap::span;
import back::link::{
    mangle_internal_name_by_path,
    mangle_internal_name_by_path_and_seq};
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
};

// ___Good to know (tm)__________________________________________________
//
// The layout of a closure environment in memory is
// roughly as follows:
//
// struct closure_box {
//    unsigned ref_count; // only used for sharid environments
//    struct closure {
//      type_desc *tydesc;         // descriptor for the "struct closure" type
//      type_desc *bound_tdescs[]; // bound descriptors
//      struct {
//          upvar1_t upvar1;
//          ...
//          upvarN_t upvarN;
//      } bound_data;
//   };
// };
//
// NB: this struct is defined in the code in trans_common::T_closure()
// and mk_closure_ty() below.  The former defines the LLVM version and
// the latter the Rust equivalent.  It occurs to me that these could
// perhaps be unified, but currently they are not.
//
// Note that the closure carries a type descriptor that describes
// itself.  Trippy.  This is needed because the precise types of the
// closed over data are lost in the closure type (`fn(T)->U`), so if
// we need to take/drop, we must know what data is in the upvars and
// so forth.
//
// The allocation strategy for this closure depends on the closure
// type.  For a sendfn, the closure (and the referenced type
// descriptors) will be allocated in the exchange heap.  For a fn, the
// closure is allocated in the task heap and is reference counted.
// For a block, the closure is allocated on the stack.  Note that in
// all cases we allocate space for a ref count just to make our lives
// easier when upcasting to block(T)->U, in the shape code, and so
// forth.
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

tag environment_value {
    // Evaluate expr and store result in env (used for bind).
    env_expr(@ast::expr);

    // Copy the value from this llvm ValueRef into the environment.
    env_copy(ValueRef, ty::t, lval_kind);

    // Move the value from this llvm ValueRef into the environment.
    env_move(ValueRef, ty::t, lval_kind);

    // Access by reference (used for blocks).
    env_ref(ValueRef, ty::t, lval_kind);
}

// Given a closure ty, emits a corresponding tuple ty
fn mk_closure_ty(tcx: ty::ctxt,
                 ck: ty::closure_kind,
                 ty_params: [fn_ty_param],
                 bound_data_ty: ty::t)
    -> ty::t {
    let tydesc_ty = alt ck {
      ty::closure_block. | ty::closure_shared. { ty::mk_type(tcx) }
      ty::closure_send. { ty::mk_send_type(tcx) }
    };
    let param_ptrs = [];
    for tp in ty_params {
        param_ptrs += [tydesc_ty];
        option::may(tp.dicts) {|dicts|
            for dict in dicts { param_ptrs += [tydesc_ty]; }
        }
    }
    ty::mk_tup(tcx, [tydesc_ty, ty::mk_tup(tcx, param_ptrs), bound_data_ty])
}

fn shared_opaque_closure_box_ty(tcx: ty::ctxt) -> ty::t {
    let opaque_closure_ty = ty::mk_opaque_closure(tcx);
    ret ty::mk_imm_box(tcx, opaque_closure_ty);
}

fn send_opaque_closure_box_ty(tcx: ty::ctxt) -> ty::t {
    let opaque_closure_ty = ty::mk_opaque_closure(tcx);
    let tup_ty = ty::mk_tup(tcx, [ty::mk_int(tcx), opaque_closure_ty]);
    ret ty::mk_uniq(tcx, {ty: tup_ty, mut: ast::imm});
}

type closure_result = {
    llbox: ValueRef,  // llvalue of boxed environment
    box_ty: ty::t,    // type of boxed environment
    bcx: @block_ctxt  // final bcx
};

// 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 store_environment(
    bcx: @block_ctxt, lltyparams: [fn_ty_param],
    bound_values: [environment_value],
    ck: ty::closure_kind)
    -> closure_result {

    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 maybe_clone_tydesc(bcx: @block_ctxt,
                          ck: ty::closure_kind,
                          td: ValueRef) -> ValueRef {
        ret alt ck {
          ty::closure_block. | ty::closure_shared. {
            td
          }
          ty::closure_send. {
            Call(bcx, bcx_ccx(bcx).upcalls.create_shared_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_copy(_, t, _) { t }
            env_move(_, t, _) { t }
            env_ref(_, t, _) { t }
            env_expr(e) { ty::expr_ty(tcx, e) }
        }];
    }
    let bound_data_ty = ty::mk_tup(tcx, bound_tys);
    let closure_ty =
        mk_closure_ty(tcx, ck, lltyparams, bound_data_ty);

    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 ck {
      ty::closure_shared. {
        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)
      }
      ty::closure_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);
        temp_cleanups += [r.val];
        dummy_environment_box(bcx, r)
      }
      ty::closure_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 ck {
      ty::closure_shared. | ty::closure_send. {
        let bound_tydesc = GEPi(bcx, closure, [0, abi::closure_elt_tydesc]);
        let ti = none;

        // NDM I believe this is the correct value,
        // but using it exposes bugs and limitations
        // in the shape code.  Therefore, I am using
        // tps_normal, which is what we used before.
        //
        // let tps = tps_fn(vec::len(lltyparams));

        let tps = tps_normal;
        let {result:closure_td, _} =
            trans::get_tydesc(bcx, closure_ty, true, tps, 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 = closure_td.bcx;
        let td = maybe_clone_tydesc(bcx, ck, closure_td.val);
        Store(bcx, td, bound_tydesc);
      }
      ty::closure_block. { /* skip this for blocks, not really relevant */ }
    }

    check type_is_tup_like(bcx, closure_ty);
    let box_ty = ty::mk_imm_box(bcx_tcx(bcx), closure_ty);

    // If necessary, copy tydescs describing type parameters into the
    // appropriate slot in the closure.
    let {bcx:bcx, val:ty_params_slot} =
        GEP_tup_like_1(bcx, closure_ty, closure,
                       [0, abi::closure_elt_ty_params]);
    let off = 0;

    for tp in lltyparams {
        let cloned_td = maybe_clone_tydesc(bcx, ck, tp.desc);
        Store(bcx, cloned_td, GEPi(bcx, ty_params_slot, [0, off]));
        off += 1;
        option::may(tp.dicts, {|dicts|
            for dict in dicts {
                let cast = PointerCast(bcx, dict, val_ty(cloned_td));
                Store(bcx, cast, GEPi(bcx, ty_params_slot, [0, off]));
                off += 1;<commit_msg>emit the take glue for closure tydescs<commit_after>import syntax::ast;
import syntax::ast_util;
import lib::llvm::llvm;
import llvm::{ValueRef, TypeRef};
import trans_common::*;
import trans_build::*;
import trans::*;
import middle::freevars::{get_freevars, freevar_info};
import option::{some, none};
import back::abi;
import syntax::codemap::span;
import back::link::{
    mangle_internal_name_by_path,
    mangle_internal_name_by_path_and_seq};
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
};

// ___Good to know (tm)__________________________________________________
//
// The layout of a closure environment in memory is
// roughly as follows:
//
// struct closure_box {
//    unsigned ref_count; // only used for sharid environments
//    struct closure {
//      type_desc *tydesc;         // descriptor for the "struct closure" type
//      type_desc *bound_tdescs[]; // bound descriptors
//      struct {
//          upvar1_t upvar1;
//          ...
//          upvarN_t upvarN;
//      } bound_data;
//   };
// };
//
// NB: this struct is defined in the code in trans_common::T_closure()
// and mk_closure_ty() below.  The former defines the LLVM version and
// the latter the Rust equivalent.  It occurs to me that these could
// perhaps be unified, but currently they are not.
//
// Note that the closure carries a type descriptor that describes
// itself.  Trippy.  This is needed because the precise types of the
// closed over data are lost in the closure type (`fn(T)->U`), so if
// we need to take/drop, we must know what data is in the upvars and
// so forth.
//
// The allocation strategy for this closure depends on the closure
// type.  For a sendfn, the closure (and the referenced type
// descriptors) will be allocated in the exchange heap.  For a fn, the
// closure is allocated in the task heap and is reference counted.
// For a block, the closure is allocated on the stack.  Note that in
// all cases we allocate space for a ref count just to make our lives
// easier when upcasting to block(T)->U, in the shape code, and so
// forth.
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

tag environment_value {
    // Evaluate expr and store result in env (used for bind).
    env_expr(@ast::expr);

    // Copy the value from this llvm ValueRef into the environment.
    env_copy(ValueRef, ty::t, lval_kind);

    // Move the value from this llvm ValueRef into the environment.
    env_move(ValueRef, ty::t, lval_kind);

    // Access by reference (used for blocks).
    env_ref(ValueRef, ty::t, lval_kind);
}

// Given a closure ty, emits a corresponding tuple ty
fn mk_closure_ty(tcx: ty::ctxt,
                 ck: ty::closure_kind,
                 ty_params: [fn_ty_param],
                 bound_data_ty: ty::t)
    -> ty::t {
    let tydesc_ty = alt ck {
      ty::closure_block. | ty::closure_shared. { ty::mk_type(tcx) }
      ty::closure_send. { ty::mk_send_type(tcx) }
    };
    let param_ptrs = [];
    for tp in ty_params {
        param_ptrs += [tydesc_ty];
        option::may(tp.dicts) {|dicts|
            for dict in dicts { param_ptrs += [tydesc_ty]; }
        }
    }
    ty::mk_tup(tcx, [tydesc_ty, ty::mk_tup(tcx, param_ptrs), bound_data_ty])
}

fn shared_opaque_closure_box_ty(tcx: ty::ctxt) -> ty::t {
    let opaque_closure_ty = ty::mk_opaque_closure(tcx);
    ret ty::mk_imm_box(tcx, opaque_closure_ty);
}

fn send_opaque_closure_box_ty(tcx: ty::ctxt) -> ty::t {
    let opaque_closure_ty = ty::mk_opaque_closure(tcx);
    let tup_ty = ty::mk_tup(tcx, [ty::mk_int(tcx), opaque_closure_ty]);
    ret ty::mk_uniq(tcx, {ty: tup_ty, mut: ast::imm});
}

type closure_result = {
    llbox: ValueRef,  // llvalue of boxed environment
    box_ty: ty::t,    // type of boxed environment
    bcx: @block_ctxt  // final bcx
};

// 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 store_environment(
    bcx: @block_ctxt, lltyparams: [fn_ty_param],
    bound_values: [environment_value],
    ck: ty::closure_kind)
    -> closure_result {

    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 maybe_clone_tydesc(bcx: @block_ctxt,
                          ck: ty::closure_kind,
                          td: ValueRef) -> ValueRef {
        ret alt ck {
          ty::closure_block. | ty::closure_shared. {
            td
          }
          ty::closure_send. {
            Call(bcx, bcx_ccx(bcx).upcalls.create_shared_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_copy(_, t, _) { t }
            env_move(_, t, _) { t }
            env_ref(_, t, _) { t }
            env_expr(e) { ty::expr_ty(tcx, e) }
        }];
    }
    let bound_data_ty = ty::mk_tup(tcx, bound_tys);
    let closure_ty =
        mk_closure_ty(tcx, ck, lltyparams, bound_data_ty);

    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 ck {
      ty::closure_shared. {
        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)
      }
      ty::closure_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);
        temp_cleanups += [r.val];
        dummy_environment_box(bcx, r)
      }
      ty::closure_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 ck {
      ty::closure_shared. | ty::closure_send. {
        let bound_tydesc = GEPi(bcx, closure, [0, abi::closure_elt_tydesc]);
        let ti = none;

        // NDM I believe this is the correct value,
        // but using it exposes bugs and limitations
        // in the shape code.  Therefore, I am using
        // tps_normal, which is what we used before.
        //
        // let tps = tps_fn(vec::len(lltyparams));

        let tps = tps_normal;
        let {result:closure_td, _} =
            trans::get_tydesc(bcx, closure_ty, true, tps, ti);
        trans::lazily_emit_tydesc_glue(bcx, abi::tydesc_field_take_glue, 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 = closure_td.bcx;
        let td = maybe_clone_tydesc(bcx, ck, closure_td.val);
        Store(bcx, td, bound_tydesc);
      }
      ty::closure_block. { /* skip this for blocks, not really relevant */ }
    }

    check type_is_tup_like(bcx, closure_ty);
    let box_ty = ty::mk_imm_box(bcx_tcx(bcx), closure_ty);

    // If necessary, copy tydescs describing type parameters into the
    // appropriate slot in the closure.
    let {bcx:bcx, val:ty_params_slot} =
        GEP_tup_like_1(bcx, closure_ty, closure,
                       [0, abi::closure_elt_ty_params]);
    let off = 0;

    for tp in lltyparams {
        let cloned_td = maybe_clone_tydesc(bcx, ck, tp.desc);
        Store(bcx, cloned_td, GEPi(bcx, ty_params_slot, [0, off]));
        off += 1;
        option::may(tp.dicts, {|dicts|
            for dict in dicts {
                let cast = PointerCast(bcx, dict, val_ty(cloned_td));
                Store(bcx, cast, GEPi(bcx, ty_params_slot, [0, off]));
                off += 1;
{'fraction_non_alphanumeric': 0.12333944750152855, 'fraction_numerical': 0.0017786671113334445, 'mean_word_length': 2.56984126984127, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5428243', 'n_tokens_mistral': 6076, 'n_tokens_neox': 6039, 'n_words': 1764}