Do not store a tag num for single-variant tags

This is a preparation for tags-as-nominal-types. A tag that has only a
single variant is now represented, at run-time, as simply a tuple of
the variant's parameters, with the variant id left off.
This commit is contained in:
Marijn Haverbeke 2011-07-01 12:36:49 +02:00
parent bf20fe5575
commit fb72be0a89
1 changed files with 144 additions and 121 deletions

View File

@ -809,14 +809,7 @@ fn type_of_inner(&@crate_ctxt cx, &span sp, &ty::t t) -> TypeRef {
case (ty::ty_char) { llty = T_char(); }
case (ty::ty_str) { llty = T_ptr(T_str()); }
case (ty::ty_istr) { llty = T_ivec(T_i8()); }
case (ty::ty_tag(_, _)) {
if (ty::type_has_dynamic_size(cx.tcx, t)) {
llty = T_opaque_tag(cx.tn);
} else {
auto size = static_size_of_tag(cx, sp, t);
llty = T_tag(cx.tn, size);
}
}
case (ty::ty_tag(?did, _)) { llty = type_of_tag(cx, sp, did, t); }
case (ty::ty_box(?mt)) {
llty = T_ptr(T_box(type_of_inner(cx, sp, mt.ty)));
}
@ -894,6 +887,22 @@ fn type_of_inner(&@crate_ctxt cx, &span sp, &ty::t t) -> TypeRef {
ret llty;
}
fn type_of_tag(&@crate_ctxt cx, &span sp, &ast::def_id did, &ty::t t)
-> TypeRef {
auto degen = vec::len(ty::tag_variants(cx.tcx, did)) == 1u;
if (ty::type_has_dynamic_size(cx.tcx, t)) {
if (degen) { ret T_i8(); }
else { ret T_opaque_tag(cx.tn); }
} else {
auto size = static_size_of_tag(cx, sp, t);
if (!degen) { ret T_tag(cx.tn, size); }
// LLVM does not like 0-size arrays, apparently
if (size == 0u) { size = 1u; }
ret T_array(T_i8(), size);
}
}
fn type_of_arg(@local_ctxt cx, &span sp, &ty::arg arg) -> TypeRef {
alt (ty::struct(cx.ccx.tcx, arg.ty)) {
case (ty::ty_param(_)) {
@ -1339,7 +1348,9 @@ fn dynamic_size_of(&@block_ctxt cx, ty::t t) -> result {
bcx.build.Store(umax(bcx, this_size, old_max_size), max_size);
}
auto max_size_val = bcx.build.Load(max_size);
auto total_size = bcx.build.Add(max_size_val, llsize_of(T_int()));
auto total_size = if (vec::len(variants) != 1u) {
bcx.build.Add(max_size_val, llsize_of(T_int()))
} else { max_size_val };
ret rslt(bcx, total_size);
}
case (ty::ty_ivec(?mt)) {
@ -2535,6 +2546,7 @@ fn iter_structural_ty_full(&@block_ctxt cx, ValueRef av, ValueRef bv,
r.bcx.build.Br(next_cx.llbb);
ret rslt(next_cx, C_nil());
}
fn iter_ivec(@block_ctxt bcx, ValueRef av, ValueRef bv, ty::t unit_ty,
&val_pair_and_ty_fn f) -> result {
// FIXME: "unimplemented rebinding existing function" workaround
@ -2603,6 +2615,42 @@ fn iter_structural_ty_full(&@block_ctxt cx, ValueRef av, ValueRef bv,
ret rslt(next_cx, C_nil());
}
fn iter_variant(@block_ctxt cx, ValueRef a_tup, ValueRef b_tup,
&ty::variant_info variant, &vec[ty::t] tps,
&ast::def_id tid, &val_pair_and_ty_fn f) -> result {
if (vec::len[ty::t](variant.args) == 0u) {
ret rslt(cx, C_nil());
}
auto fn_ty = variant.ctor_ty;
auto ccx = cx.fcx.lcx.ccx;
alt (ty::struct(ccx.tcx, fn_ty)) {
case (ty::ty_fn(_, ?args, _, _, _)) {
auto j = 0;
for (ty::arg a in args) {
auto rslt = GEP_tag(cx, a_tup, tid,
variant.id, tps, j);
auto llfldp_a = rslt.val;
cx = rslt.bcx;
rslt = GEP_tag(cx, b_tup, tid,
variant.id, tps, j);
auto llfldp_b = rslt.val;
cx = rslt.bcx;
auto ty_subst =
ty::substitute_type_params(ccx.tcx, tps, a.ty);
auto llfld_a =
load_if_immediate(cx, llfldp_a, ty_subst);
auto llfld_b =
load_if_immediate(cx, llfldp_b, ty_subst);
rslt = f(cx, llfld_a, llfld_b, ty_subst);
cx = rslt.bcx;
j += 1;
}
}
}
ret rslt(cx, C_nil());
}
let result r = rslt(cx, C_nil());
alt (ty::struct(cx.fcx.lcx.ccx.tcx, t)) {
case (ty::ty_tup(?args)) {
@ -2612,8 +2660,7 @@ fn iter_structural_ty_full(&@block_ctxt cx, ValueRef av, ValueRef bv,
auto elt_a = r.val;
r = GEP_tup_like(r.bcx, t, bv, [0, i]);
auto elt_b = r.val;
r =
f(r.bcx, load_if_immediate(r.bcx, elt_a, arg.ty),
r = f(r.bcx, load_if_immediate(r.bcx, elt_a, arg.ty),
load_if_immediate(r.bcx, elt_b, arg.ty), arg.ty);
i += 1;
}
@ -2625,8 +2672,7 @@ fn iter_structural_ty_full(&@block_ctxt cx, ValueRef av, ValueRef bv,
auto llfld_a = r.val;
r = GEP_tup_like(r.bcx, t, bv, [0, i]);
auto llfld_b = r.val;
r =
f(r.bcx, load_if_immediate(r.bcx, llfld_a, fld.mt.ty),
r = f(r.bcx, load_if_immediate(r.bcx, llfld_a, fld.mt.ty),
load_if_immediate(r.bcx, llfld_b, fld.mt.ty),
fld.mt.ty);
i += 1;
@ -2644,8 +2690,12 @@ fn iter_structural_ty_full(&@block_ctxt cx, ValueRef av, ValueRef bv,
}
case (ty::ty_tag(?tid, ?tps)) {
auto variants = ty::tag_variants(cx.fcx.lcx.ccx.tcx, tid);
auto n_variants = vec::len[ty::variant_info](variants);
auto n_variants = vec::len(variants);
// Cast the tags to types we can GEP into.
if (n_variants == 1u) {
ret iter_variant(cx, av, bv, variants.(0), tps, tid, f);
}
auto lltagty = T_opaque_tag_ptr(cx.fcx.lcx.ccx.tn);
auto av_tag = cx.build.PointerCast(av, lltagty);
@ -2656,9 +2706,9 @@ fn iter_structural_ty_full(&@block_ctxt cx, ValueRef av, ValueRef bv,
auto lldiscrim_b_ptr = cx.build.GEP(bv_tag, [C_int(0), C_int(0)]);
auto llunion_b_ptr = cx.build.GEP(bv_tag, [C_int(0), C_int(1)]);
auto lldiscrim_b = cx.build.Load(lldiscrim_b_ptr);
// NB: we must hit the discriminant first so that structural
// comparison know not to proceed when the discriminants differ.
auto bcx = cx;
bcx =
f(bcx, lldiscrim_a, lldiscrim_b,
@ -2675,47 +2725,10 @@ fn iter_structural_ty_full(&@block_ctxt cx, ValueRef av, ValueRef bv,
"tag-iter-variant-" +
uint::to_str(i, 10u));
llvm::LLVMAddCase(llswitch, C_int(i as int), variant_cx.llbb);
if (vec::len[ty::t](variant.args) > 0u) {
// N-ary variant.
auto fn_ty = variant.ctor_ty;
alt (ty::struct(bcx.fcx.lcx.ccx.tcx, fn_ty)) {
case (ty::ty_fn(_, ?args, _, _, _)) {
auto j = 0;
for (ty::arg a in args) {
auto rslt =
GEP_tag(variant_cx, llunion_a_ptr, tid,
variant.id, tps, j);
auto llfldp_a = rslt.val;
variant_cx = rslt.bcx;
rslt =
GEP_tag(variant_cx, llunion_b_ptr, tid,
variant.id, tps, j);
auto llfldp_b = rslt.val;
variant_cx = rslt.bcx;
auto tcx = cx.fcx.lcx.ccx.tcx;
auto ty_subst =
ty::substitute_type_params(tcx, tps,
a.ty);
auto llfld_a =
load_if_immediate(variant_cx, llfldp_a,
ty_subst);
auto llfld_b =
load_if_immediate(variant_cx, llfldp_b,
ty_subst);
rslt =
f(variant_cx, llfld_a, llfld_b, ty_subst);
variant_cx = rslt.bcx;
j += 1;
}
}
}
variant_cx.build.Br(next_cx.llbb);
} else {
// Nullary variant; nothing to do.
variant_cx.build.Br(next_cx.llbb);
}
variant_cx = iter_variant
(variant_cx, llunion_a_ptr, llunion_b_ptr, variant,
tps, tid, f).bcx;
variant_cx.build.Br(next_cx.llbb);
i += 1u;
}
ret rslt(next_cx, C_nil());
@ -4614,34 +4627,43 @@ fn trans_pat_match(&@block_ctxt cx, &@ast::pat pat, ValueRef llval,
ret rslt(matched_cx, llval);
}
case (ast::pat_tag(?ident, ?subpats, ?id)) {
auto lltagptr =
cx.build.PointerCast(llval,
T_opaque_tag_ptr(cx.fcx.lcx.ccx.tn));
auto lldiscrimptr = cx.build.GEP(lltagptr, [C_int(0), C_int(0)]);
auto lldiscrim = cx.build.Load(lldiscrimptr);
auto vdef =
ast::variant_def_ids(cx.fcx.lcx.ccx.tcx.def_map.get(id));
auto variant_tag = 0;
auto variants = ty::tag_variants(cx.fcx.lcx.ccx.tcx, vdef._0);
auto i = 0;
for (ty::variant_info v in variants) {
auto this_variant_id = v.id;
if (vdef._1._0 == this_variant_id._0 &&
vdef._1._1 == this_variant_id._1) {
variant_tag = i;
}
i += 1;
}
auto matched_cx = new_sub_block_ctxt(cx, "matched_cx");
auto lleq =
cx.build.ICmp(lib::llvm::LLVMIntEQ, lldiscrim,
C_int(variant_tag));
cx.build.CondBr(lleq, matched_cx.llbb, next_cx.llbb);
auto llblobptr = llval;
if (vec::len(variants) == 1u) {
cx.build.Br(matched_cx.llbb);
} else {
auto lltagptr =
cx.build.PointerCast(llval,
T_opaque_tag_ptr(cx.fcx.lcx.ccx.tn));
auto lldiscrimptr = cx.build.GEP(lltagptr,
[C_int(0), C_int(0)]);
auto lldiscrim = cx.build.Load(lldiscrimptr);
auto variant_tag = 0;
auto i = 0;
for (ty::variant_info v in variants) {
auto this_variant_id = v.id;
if (vdef._1._0 == this_variant_id._0 &&
vdef._1._1 == this_variant_id._1) {
variant_tag = i;
}
i += 1;
}
auto lleq =
cx.build.ICmp(lib::llvm::LLVMIntEQ, lldiscrim,
C_int(variant_tag));
cx.build.CondBr(lleq, matched_cx.llbb, next_cx.llbb);
if (vec::len(subpats) > 0u) {
llblobptr =
matched_cx.build.GEP(lltagptr, [C_int(0), C_int(1)]);
}
}
auto ty_params = ty::node_id_to_type_params
(cx.fcx.lcx.ccx.tcx, id);
if (vec::len[@ast::pat](subpats) > 0u) {
auto llblobptr =
matched_cx.build.GEP(lltagptr, [C_int(0), C_int(1)]);
if (vec::len(subpats) > 0u) {
auto i = 0;
for (@ast::pat subpat in subpats) {
auto rslt =
@ -4690,10 +4712,12 @@ fn trans_pat_binding(&@block_ctxt cx, &@ast::pat pat, ValueRef llval,
auto vdef =
ast::variant_def_ids(cx.fcx.lcx.ccx.tcx.def_map.get(id));
auto lltagptr =
cx.build.PointerCast(llval,
T_opaque_tag_ptr(cx.fcx.lcx.ccx.tn));
auto llblobptr = cx.build.GEP(lltagptr, [C_int(0), C_int(1)]);
auto llblobptr = llval;
if (vec::len(ty::tag_variants(cx.fcx.lcx.ccx.tcx, vdef._0))!=1u) {
auto lltagptr = cx.build.PointerCast
(llval, T_opaque_tag_ptr(cx.fcx.lcx.ccx.tn));
llblobptr = cx.build.GEP(lltagptr, [C_int(0), C_int(1)]);
}
auto ty_param_substs =
ty::node_id_to_type_params(cx.fcx.lcx.ccx.tcx, id);
auto this_cx = cx;
@ -4828,6 +4852,7 @@ fn lookup_discriminant(&@local_ctxt lcx, &ast::def_id tid, &ast::def_id vid)
}
fn trans_path(&@block_ctxt cx, &ast::path p, ast::node_id id) -> lval_result {
auto ccx = cx.fcx.lcx.ccx;
alt (cx.fcx.lcx.ccx.tcx.def_map.get(id)) {
case (ast::def_arg(?did)) {
alt (cx.fcx.llargs.find(did._1)) {
@ -4856,12 +4881,12 @@ fn trans_path(&@block_ctxt cx, &ast::path p, ast::node_id id) -> lval_result {
ret lval_mem(cx, cx.fcx.llobjfields.get(did._1));
}
case (ast::def_fn(?did, _)) {
auto tyt = ty::lookup_item_type(cx.fcx.lcx.ccx.tcx, did);
auto tyt = ty::lookup_item_type(ccx.tcx, did);
ret lval_generic_fn(cx, tyt, did, id);
}
case (ast::def_variant(?tid, ?vid)) {
auto v_tyt = ty::lookup_item_type(cx.fcx.lcx.ccx.tcx, vid);
alt (ty::struct(cx.fcx.lcx.ccx.tcx, v_tyt._1)) {
auto v_tyt = ty::lookup_item_type(ccx.tcx, vid);
alt (ty::struct(ccx.tcx, v_tyt._1)) {
case (ty::ty_fn(_, _, _, _, _)) {
// N-ary variant.
@ -4869,43 +4894,36 @@ fn trans_path(&@block_ctxt cx, &ast::path p, ast::node_id id) -> lval_result {
}
case (_) {
// Nullary variant.
auto tag_ty = node_id_type(cx.fcx.lcx.ccx, id);
auto lldiscrim_gv =
lookup_discriminant(cx.fcx.lcx, tid, vid);
auto lldiscrim = cx.build.Load(lldiscrim_gv);
auto tag_ty = node_id_type(ccx, id);
auto alloc_result = alloc_ty(cx, tag_ty);
auto lltagblob = alloc_result.val;
auto lltagty;
if (ty::type_has_dynamic_size(cx.fcx.lcx.ccx.tcx, tag_ty))
{
lltagty = T_opaque_tag(cx.fcx.lcx.ccx.tn);
} else {
lltagty = type_of(cx.fcx.lcx.ccx, p.span, tag_ty);
auto lltagty = type_of_tag(ccx, p.span, tid, tag_ty);
auto bcx = alloc_result.bcx;
auto lltagptr = bcx.build.PointerCast
(lltagblob, T_ptr(lltagty));
if (vec::len(ty::tag_variants(ccx.tcx, tid)) != 1u) {
auto lldiscrim_gv =
lookup_discriminant(bcx.fcx.lcx, tid, vid);
auto lldiscrim = bcx.build.Load(lldiscrim_gv);
auto lldiscrimptr = bcx.build.GEP
(lltagptr, [C_int(0), C_int(0)]);
bcx.build.Store(lldiscrim, lldiscrimptr);
}
auto lltagptr =
alloc_result.bcx.build.PointerCast(lltagblob,
T_ptr(lltagty));
auto lldiscrimptr =
alloc_result.bcx.build.GEP(lltagptr,
[C_int(0), C_int(0)]);
alloc_result.bcx.build.Store(lldiscrim, lldiscrimptr);
ret lval_val(alloc_result.bcx, lltagptr);
ret lval_val(bcx, lltagptr);
}
}
}
case (ast::def_const(?did)) {
// TODO: externals
assert (cx.fcx.lcx.ccx.consts.contains_key(did._1));
ret lval_mem(cx, cx.fcx.lcx.ccx.consts.get(did._1));
assert (ccx.consts.contains_key(did._1));
ret lval_mem(cx, ccx.consts.get(did._1));
}
case (ast::def_native_fn(?did)) {
auto tyt = ty::lookup_item_type(cx.fcx.lcx.ccx.tcx, did);
auto tyt = ty::lookup_item_type(ccx.tcx, did);
ret lval_generic_fn(cx, tyt, did, id);
}
case (_) {
cx.fcx.lcx.ccx.sess.span_unimpl(cx.sp, "def variant in trans");
ccx.sess.span_unimpl(cx.sp, "def variant in trans");
}
}
}
@ -5538,7 +5556,6 @@ fn trans_arg_expr(&@block_ctxt cx, &ty::arg arg, TypeRef lldestty0,
// "undef" value, as such a value should never
// be inspected. It's important for the value
// to have type lldestty0 (the callee's expected type).
val = llvm::LLVMGetUndef(lldestty0);
} else if (ty::type_contains_params(cx.fcx.lcx.ccx.tcx, arg.ty)) {
auto lldestty = lldestty0;
@ -7920,7 +7937,7 @@ fn trans_res_ctor(@local_ctxt cx, &span sp, &ast::_fn dtor,
fn trans_tag_variant(@local_ctxt cx, ast::node_id tag_id,
&ast::variant variant, int index,
&ast::variant variant, int index, bool is_degen,
&vec[ast::ty_param] ty_params) {
if (vec::len[ast::variant_arg](variant.node.args) == 0u) {
ret; // nullary constructors are just constants
@ -7953,13 +7970,17 @@ fn trans_tag_variant(@local_ctxt cx, ast::node_id tag_id,
copy_args_to_allocas(fcx, fn_args, arg_tys);
auto bcx = new_top_block_ctxt(fcx);
auto lltop = bcx.llbb;
// Cast the tag to a type we can GEP into.
auto lltagptr =
bcx.build.PointerCast(fcx.llretptr, T_opaque_tag_ptr(fcx.lcx.ccx.tn));
auto lldiscrimptr = bcx.build.GEP(lltagptr, [C_int(0), C_int(0)]);
bcx.build.Store(C_int(index), lldiscrimptr);
auto llblobptr = bcx.build.GEP(lltagptr, [C_int(0), C_int(1)]);
auto llblobptr = if (is_degen) {
fcx.llretptr
} else {
// Cast the tag to a type we can GEP into.
auto lltagptr = bcx.build.PointerCast
(fcx.llretptr, T_opaque_tag_ptr(fcx.lcx.ccx.tn));
auto lldiscrimptr = bcx.build.GEP(lltagptr, [C_int(0), C_int(0)]);
bcx.build.Store(C_int(index), lldiscrimptr);
bcx.build.GEP(lltagptr, [C_int(0), C_int(1)])
};
i = 0u;
for (ast::variant_arg va in variant.node.args) {
auto rslt =
@ -8040,9 +8061,10 @@ fn trans_item(@local_ctxt cx, &ast::item item) {
}
case (ast::item_tag(?variants, ?tps)) {
auto sub_cx = extend_path(cx, item.ident);
auto degen = vec::len(variants) == 1u;
auto i = 0;
for (ast::variant variant in variants) {
trans_tag_variant(sub_cx, item.id, variant, i, tps);
trans_tag_variant(sub_cx, item.id, variant, i, degen, tps);
i += 1;
}
}
@ -8427,13 +8449,14 @@ fn trans_constant(@crate_ctxt ccx, &@ast::item it, &vec[str] pt,
auto n_variants = vec::len[ast::variant](variants);
while (i < n_variants) {
auto variant = variants.(i);
auto discrim_val = C_int(i as int);
auto p = new_pt + [it.ident, variant.node.name, "discrim"];
auto s = mangle_exported_name(ccx, p, ty::mk_int(ccx.tcx));
auto discrim_gvar =
llvm::LLVMAddGlobal(ccx.llmod, T_int(), str::buf(s));
llvm::LLVMSetInitializer(discrim_gvar, discrim_val);
llvm::LLVMSetGlobalConstant(discrim_gvar, True);
if (n_variants != 1u) {
llvm::LLVMSetInitializer(discrim_gvar, C_int(i as int));
llvm::LLVMSetGlobalConstant(discrim_gvar, True);
}
ccx.discrims.insert(variant.node.id, discrim_gvar);
ccx.discrim_symbols.insert(variant.node.id, s);
i += 1u;