Allow dereferencing of single-variant, single-argument tag values

(Using the * operator.)

This makes tags more useful as nominal 'newtype' types, since you no
longer have to copy out their contents (or construct a cumbersome
boilerplate alt) to access them.

I could have gone with a scheme where you could dereference individual
arguments of an n-ary variant with ._0, ._1, etc, but opted not to,
since we plan to move to a system where all variants are unary (or, I
guess, nullary).
This commit is contained in:
Marijn Haverbeke 2011-07-01 13:57:03 +02:00
parent 57e6340253
commit 432e5e9f7f
4 changed files with 141 additions and 81 deletions

View File

@ -549,18 +549,13 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef) ->
case (ast::expr_unary(?op, ?base)) {
if (op == ast::deref) {
auto base_t = ty::expr_ty(*cx.tcx, base);
auto mut = false;
alt (ty::struct(*cx.tcx, base_t)) {
case (ty::ty_box(?mt)) {
vec::push(ds, rec(mut=mt.mut != ast::imm,
kind=unbox,
outer_t=base_t));
}
case (ty::ty_res(_, ?inner, _)) {
vec::push(ds, rec(mut=false,
kind=unbox,
outer_t=base_t));
}
case (ty::ty_box(?mt)) { mut = mt.mut != ast::imm; }
case (ty::ty_res(_, _, _)) {}
case (ty::ty_tag(_, _)) {}
}
vec::push(ds, rec(mut=mut, kind=unbox, outer_t=base_t));
ex = base;
} else { break; }
}

View File

@ -329,6 +329,7 @@ type block_ctxt =
tag block_parent { parent_none; parent_some(@block_ctxt); }
type result = rec(@block_ctxt bcx, ValueRef val);
type result_t = rec(@block_ctxt bcx, ValueRef val, ty::t ty);
fn extend_path(@local_ctxt cx, &str name) -> @local_ctxt {
ret @rec(path=cx.path + [name] with *cx);
@ -3320,18 +3321,16 @@ fn trans_unary(&@block_ctxt cx, ast::unop op, &@ast::expr e,
auto e_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, e);
alt (op) {
case (ast::not) {
sub =
autoderef(sub.bcx, sub.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, e));
ret rslt(sub.bcx, sub.bcx.build.Not(sub.val));
auto dr = autoderef(sub.bcx, sub.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, e));
ret rslt(dr.bcx, dr.bcx.build.Not(dr.val));
}
case (ast::neg) {
sub =
autoderef(sub.bcx, sub.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, e));
auto dr = autoderef(sub.bcx, sub.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, e));
if (ty::struct(cx.fcx.lcx.ccx.tcx, e_ty) == ty::ty_float) {
ret rslt(sub.bcx, sub.bcx.build.FNeg(sub.val));
} else { ret rslt(sub.bcx, sub.bcx.build.Neg(sub.val)); }
ret rslt(dr.bcx, dr.bcx.build.FNeg(dr.val));
} else { ret rslt(dr.bcx, sub.bcx.build.Neg(dr.val)); }
}
case (ast::box(_)) {
auto e_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, e);
@ -3380,7 +3379,6 @@ fn trans_compare(&@block_ctxt cx0, ast::binop op, &ty::t t0, ValueRef lhs0,
auto rhs_r = autoderef(cx, rhs0, t0);
auto rhs = rhs_r.val;
cx = rhs_r.bcx;
auto t = ty::type_autoderef(cx.fcx.lcx.ccx.tcx, t0);
// Determine the operation we need.
// FIXME: Use or-patterns when we have them.
@ -3393,7 +3391,7 @@ fn trans_compare(&@block_ctxt cx0, ast::binop op, &ty::t t0, ValueRef lhs0,
case (ast::ge) { llop = C_u8(abi::cmp_glue_op_lt); }
case (ast::gt) { llop = C_u8(abi::cmp_glue_op_le); }
}
auto rs = compare(cx, lhs, rhs, t, llop);
auto rs = compare(cx, lhs, rhs, rhs_r.ty, llop);
// Invert the result if necessary.
// FIXME: Use or-patterns when we have them.
@ -4113,11 +4111,12 @@ fn trans_eager_binop(&@block_ctxt cx, ast::binop op, &ty::t intype,
}
fn autoderef_lval(&@block_ctxt cx, ValueRef v, &ty::t t, bool is_lval)
-> result {
-> result_t {
let ValueRef v1 = v;
let ty::t t1 = t;
auto ccx = cx.fcx.lcx.ccx;
while (true) {
alt (ty::struct(cx.fcx.lcx.ccx.tcx, t1)) {
alt (ty::struct(ccx.tcx, t1)) {
case (ty::ty_box(?mt)) {
// If we are working with an lval, we want to
// unconditionally load at the top of the loop
@ -4132,24 +4131,41 @@ fn autoderef_lval(&@block_ctxt cx, ValueRef v, &ty::t t, bool is_lval)
// to cast this pointer, since statically-sized tag types have
// different types depending on whether they're behind a box
// or not.
if (!ty::type_has_dynamic_size(cx.fcx.lcx.ccx.tcx, mt.ty)) {
auto llty = type_of(cx.fcx.lcx.ccx, cx.sp, mt.ty);
if (!ty::type_has_dynamic_size(ccx.tcx, mt.ty)) {
auto llty = type_of(ccx, cx.sp, mt.ty);
v1 = cx.build.PointerCast(body, T_ptr(llty));
} else { v1 = body; }
// But if we aren't working with an lval, we get rid of
// a layer of indirection at the bottom of the loop so
// that it is gone when we return...
if (!is_lval) { v1 = load_if_immediate(cx, v1, t1); }
}
case (ty::ty_res(?did, ?inner, ?tps)) {
if (is_lval) { v1 = cx.build.Load(v1); }
t1 = ty::substitute_type_params(ccx.tcx, tps, inner);
v1 = cx.build.GEP(v1, [C_int(0), C_int(1)]);
}
case (ty::ty_tag(?did, ?tps)) {
auto variants = ty::tag_variants(ccx.tcx, did);
if (vec::len(variants) != 1u ||
vec::len(variants.(0).args) != 1u) {
break;
}
if (is_lval) { v1 = cx.build.Load(v1); }
t1 = ty::substitute_type_params
(ccx.tcx, tps, variants.(0).args.(0));
if (!ty::type_has_dynamic_size(ccx.tcx, t1)) {
v1 = cx.build.PointerCast
(v1, T_ptr(type_of(ccx, cx.sp, t1)));
}
}
case (_) { break; }
}
// But if we aren't working with an lval, we get rid of
// a layer of indirection at the bottom of the loop so
// that it is gone when we return...
if (!is_lval) { v1 = load_if_immediate(cx, v1, t1); }
}
ret rslt(cx, v1);
ret rec(bcx=cx, val=v1, ty=t1);
}
fn autoderef(&@block_ctxt cx, ValueRef v, &ty::t t) -> result {
fn autoderef(&@block_ctxt cx, ValueRef v, &ty::t t) -> result_t {
ret autoderef_lval(cx, v, t, false);
}
@ -4160,15 +4176,14 @@ fn trans_binary(&@block_ctxt cx, ast::binop op, &@ast::expr a, &@ast::expr b)
alt (op) {
case (ast::and) {
// Lazy-eval and
auto lhs_res = trans_expr(cx, a);
lhs_res =
autoderef(lhs_res.bcx, lhs_res.val,
auto lhs_expr = trans_expr(cx, a);
auto lhs_res =
autoderef(lhs_expr.bcx, lhs_expr.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, a));
auto rhs_cx = new_scope_block_ctxt(cx, "rhs");
auto rhs_res = trans_expr(rhs_cx, b);
rhs_res =
autoderef(rhs_res.bcx, rhs_res.val,
auto rhs_expr = trans_expr(rhs_cx, b);
auto rhs_res =
autoderef(rhs_expr.bcx, rhs_expr.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, b));
auto lhs_false_cx = new_scope_block_ctxt(cx, "lhs false");
auto lhs_false_res = rslt(lhs_false_cx, C_bool(false));
@ -4181,20 +4196,18 @@ fn trans_binary(&@block_ctxt cx, ast::binop op, &@ast::expr a, &@ast::expr b)
lhs_res.bcx.build.CondBr(lhs_res.val, rhs_cx.llbb,
lhs_false_cx.llbb);
ret join_results(cx, T_bool(),
[lhs_false_res, rec(bcx=rhs_bcx with rhs_res)]);
[lhs_false_res, rec(bcx=rhs_bcx,
val=rhs_res.val)]);
}
case (ast::or) {
// Lazy-eval or
auto lhs_res = trans_expr(cx, a);
lhs_res =
autoderef(lhs_res.bcx, lhs_res.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, a));
auto lhs_expr = trans_expr(cx, a);
auto lhs_res = autoderef(lhs_expr.bcx, lhs_expr.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, a));
auto rhs_cx = new_scope_block_ctxt(cx, "rhs");
auto rhs_res = trans_expr(rhs_cx, b);
rhs_res =
autoderef(rhs_res.bcx, rhs_res.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, b));
auto rhs_expr = trans_expr(rhs_cx, b);
auto rhs_res = autoderef(rhs_expr.bcx, rhs_expr.val,
ty::expr_ty(cx.fcx.lcx.ccx.tcx, b));
auto lhs_true_cx = new_scope_block_ctxt(cx, "lhs true");
auto lhs_true_res = rslt(lhs_true_cx, C_bool(true));
// see the and case for an explanation
@ -4203,19 +4216,19 @@ fn trans_binary(&@block_ctxt cx, ast::binop op, &@ast::expr a, &@ast::expr b)
lhs_res.bcx.build.CondBr(lhs_res.val, lhs_true_cx.llbb,
rhs_cx.llbb);
ret join_results(cx, T_bool(),
[lhs_true_res, rec(bcx=rhs_bcx with rhs_res)]);
[lhs_true_res, rec(bcx=rhs_bcx,
val=rhs_res.val)]);
}
case (_) {
// Remaining cases are eager:
auto lhs = trans_expr(cx, a);
auto lhs_expr = trans_expr(cx, a);
auto lhty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, a);
lhs = autoderef(lhs.bcx, lhs.val, lhty);
auto rhs = trans_expr(lhs.bcx, b);
auto lhs = autoderef(lhs_expr.bcx, lhs_expr.val, lhty);
auto rhs_expr = trans_expr(lhs.bcx, b);
auto rhty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, b);
rhs = autoderef(rhs.bcx, rhs.val, rhty);
ret trans_eager_binop(rhs.bcx, op,
ty::type_autoderef(cx.fcx.lcx.ccx.tcx,lhty),
auto rhs = autoderef(rhs_expr.bcx, rhs_expr.val, rhty);
ret trans_eager_binop(rhs.bcx, op, lhs.ty,
lhs.val, rhs.val);
}
}
@ -4931,7 +4944,7 @@ fn trans_path(&@block_ctxt cx, &ast::path p, ast::node_id id) -> lval_result {
fn trans_field(&@block_ctxt cx, &span sp, ValueRef v, &ty::t t0,
&ast::ident field, ast::node_id id) -> lval_result {
auto r = autoderef(cx, v, t0);
auto t = ty::type_autoderef(cx.fcx.lcx.ccx.tcx, t0);
auto t = r.ty;
alt (ty::struct(cx.fcx.lcx.ccx.tcx, t)) {
case (ty::ty_tup(_)) {
let uint ix = ty::field_num(cx.fcx.lcx.ccx.sess, sp, field);
@ -4971,11 +4984,11 @@ fn trans_index(&@block_ctxt cx, &span sp, &@ast::expr base, &@ast::expr idx,
// Is this an interior vector?
auto base_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, base);
auto base_ty_no_boxes = ty::strip_boxes(cx.fcx.lcx.ccx.tcx, base_ty);
auto exp = trans_expr(cx, base);
auto lv = autoderef(exp.bcx, exp.val, base_ty);
auto base_ty_no_boxes = lv.ty;
auto is_interior =
ty::sequence_is_interior(cx.fcx.lcx.ccx.tcx, base_ty_no_boxes);
auto lv = trans_expr(cx, base);
lv = autoderef(lv.bcx, lv.val, base_ty);
auto ix = trans_expr(lv.bcx, idx);
auto v = lv.val;
auto bcx = ix.bcx;
@ -5056,14 +5069,29 @@ fn trans_lval(&@block_ctxt cx, &@ast::expr e) -> lval_result {
ret trans_index(cx, e.span, base, idx, e.id);
}
case (ast::expr_unary(?unop, ?base)) {
auto ccx = cx.fcx.lcx.ccx;
assert (unop == ast::deref);
auto sub = trans_expr(cx, base);
auto t = ty::expr_ty(cx.fcx.lcx.ccx.tcx, base);
auto offset = alt (ty::struct(cx.fcx.lcx.ccx.tcx, t)) {
case (ty::ty_box(_)) { abi::box_rc_field_body }
case (ty::ty_res(_, _, _)) { 1 }
auto t = ty::expr_ty(ccx.tcx, base);
auto val = alt (ty::struct(ccx.tcx, t)) {
case (ty::ty_box(_)) {
sub.bcx.build.GEP
(sub.val, [C_int(0), C_int(abi::box_rc_field_body)])
}
case (ty::ty_res(_, _, _)) {
sub.bcx.build.GEP(sub.val, [C_int(0), C_int(1)])
}
case (ty::ty_tag(_, _)) {
auto ety = ty::expr_ty(ccx.tcx, e);
auto ellty;
if (ty::type_has_dynamic_size(ccx.tcx, ety)) {
ellty = T_typaram_ptr(ccx.tn);
} else {
ellty = T_ptr(type_of(ccx, e.span, ety));
};
sub.bcx.build.PointerCast(sub.val, ellty)
}
};
auto val = sub.bcx.build.GEP(sub.val, [C_int(0), C_int(offset)]);
ret lval_mem(sub.bcx, val);
}
case (ast::expr_self_method(?ident)) {
@ -5692,7 +5720,7 @@ fn trans_call(&@block_ctxt cx, &@ast::expr f, &option::t[ValueRef] lliterbody,
// It's a closure. We have to autoderef.
auto res = autoderef_lval(bcx, f_res.res.val, fn_ty, true);
bcx = res.bcx;
fn_ty = ty::type_autoderef(bcx.fcx.lcx.ccx.tcx, fn_ty);
fn_ty = res.ty;
auto pair = res.val;
faddr =

View File

@ -111,7 +111,6 @@ export sequence_is_interior;
export struct;
export sort_methods;
export stmt_node_id;
export strip_boxes;
export sty;
export substitute_type_params;
export t;
@ -1287,6 +1286,17 @@ fn type_autoderef(&ctxt cx, &ty::t t) -> ty::t {
while (true) {
alt (struct(cx, t1)) {
case (ty::ty_box(?mt)) { t1 = mt.ty; }
case (ty::ty_res(_, ?inner, ?tps)) {
t1 = substitute_type_params(cx, tps, inner);
}
case (ty::ty_tag(?did, ?tps)) {
auto variants = tag_variants(cx, did);
if (vec::len(variants) != 1u ||
vec::len(variants.(0).args) != 1u) {
break;
}
t1 = substitute_type_params(cx, tps, variants.(0).args.(0));
}
case (_) { break; }
}
}
@ -2891,7 +2901,7 @@ fn ret_ty_of_fn(ctxt cx, ast::node_id id) -> t {
// NB: This function requires that the given type has no variables. So, inside
// typeck, you should use typeck::strip_boxes() instead.
// typeck, you should use typeck::do_autoderef() instead.
fn strip_boxes(&ctxt cx, &ty::t t) -> ty::t {
auto t1 = t;
while (true) {

View File

@ -819,11 +819,25 @@ mod unify {
tag autoderef_kind { AUTODEREF_OK; NO_AUTODEREF; }
fn strip_boxes(&@fn_ctxt fcx, &span sp, &ty::t t) -> ty::t {
// FIXME This is almost a duplicate of ty::type_autoderef, with structure_of
// instead of ty::struct.
fn do_autoderef(&@fn_ctxt fcx, &span sp, &ty::t t) -> ty::t {
auto t1 = t;
while (true) {
alt (structure_of(fcx, sp, t1)) {
case (ty::ty_box(?inner)) { t1 = inner.ty; }
case (ty::ty_res(_, ?inner, ?tps)) {
t1 = ty::substitute_type_params(fcx.ccx.tcx, tps, inner);
}
case (ty::ty_tag(?did, ?tps)) {
auto variants = ty::tag_variants(fcx.ccx.tcx, did);
if (vec::len(variants) != 1u ||
vec::len(variants.(0).args) != 1u) {
ret t1;
}
t1 = ty::substitute_type_params(fcx.ccx.tcx, tps,
variants.(0).args.(0));
}
case (_) { ret t1; }
}
}
@ -881,8 +895,8 @@ mod demand {
auto actual_1 = actual;
auto implicit_boxes = 0u;
if (adk == AUTODEREF_OK) {
expected_1 = strip_boxes(fcx, sp, expected_1);
actual_1 = strip_boxes(fcx, sp, actual_1);
expected_1 = do_autoderef(fcx, sp, expected_1);
actual_1 = do_autoderef(fcx, sp, actual_1);
implicit_boxes = count_boxes(fcx, sp, actual);
}
let vec[mutable ty::t] ty_param_substs = [mutable ];
@ -1346,7 +1360,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
// We want to autoderef calls but not binds
auto fty_stripped =
if (is_call) { strip_boxes(fcx, sp, fty) } else { fty };
if (is_call) { do_autoderef(fcx, sp, fty) } else { fty };
// Grab the argument types and the return type.
auto arg_tys;
@ -1534,7 +1548,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
case (ast::ne) { ty::mk_bool(fcx.ccx.tcx) }
case (ast::ge) { ty::mk_bool(fcx.ccx.tcx) }
case (ast::gt) { ty::mk_bool(fcx.ccx.tcx) }
case (_) { strip_boxes(fcx, expr.span, lhs_t) }
case (_) { do_autoderef(fcx, expr.span, lhs_t) }
};
write::ty_only_fixup(fcx, id, t);
}
@ -1549,10 +1563,23 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
alt (structure_of(fcx, expr.span, oper_t)) {
case (ty::ty_box(?inner)) { oper_t = inner.ty; }
case (ty::ty_res(_, ?inner, _)) { oper_t = inner; }
case (ty::ty_tag(?id, ?tps)) {
auto variants = ty::tag_variants(fcx.ccx.tcx, id);
if (vec::len(variants) != 1u ||
vec::len(variants.(0).args) != 1u) {
fcx.ccx.tcx.sess.span_fatal
(expr.span, "can only dereference tags " +
"with a single variant which has a " +
"single argument");
}
oper_t = ty::substitute_type_params
(fcx.ccx.tcx, tps, variants.(0).args.(0));
}
case (_) {
auto s = "dereferencing non-box type: " +
ty_to_str(fcx.ccx.tcx, oper_t);
fcx.ccx.tcx.sess.span_fatal(expr.span, s);
fcx.ccx.tcx.sess.span_fatal
(expr.span, "dereferencing non-" +
"dereferenceable type: " +
ty_to_str(fcx.ccx.tcx, oper_t));
}
}
}
@ -1568,7 +1595,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
oper_t)));
}
}
case (_) { oper_t = strip_boxes(fcx, expr.span, oper_t); }
case (_) { oper_t = do_autoderef(fcx, expr.span, oper_t); }
}
write::ty_only_fixup(fcx, id, oper_t);
}
@ -1859,7 +1886,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
// Pull the return type out of the type of the function.
auto rt_1;
auto fty = strip_boxes(fcx, expr.span,
auto fty = do_autoderef(fcx, expr.span,
ty::expr_ty(fcx.ccx.tcx, f));
alt (structure_of(fcx, expr.span, fty)) {
case (ty::ty_fn(_, _, ?rt, _, _)) { rt_1 = rt; }
@ -2017,7 +2044,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
case (ast::expr_field(?base, ?field)) {
check_expr(fcx, base);
auto base_t = expr_ty(fcx.ccx.tcx, base);
base_t = strip_boxes(fcx, expr.span, base_t);
base_t = do_autoderef(fcx, expr.span, base_t);
alt (structure_of(fcx, expr.span, base_t)) {
case (ty::ty_tup(?args)) {
let uint ix =
@ -2063,7 +2090,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
case (ast::expr_index(?base, ?idx)) {
check_expr(fcx, base);
auto base_t = expr_ty(fcx.ccx.tcx, base);
base_t = strip_boxes(fcx, expr.span, base_t);
base_t = do_autoderef(fcx, expr.span, base_t);
check_expr(fcx, idx);
auto idx_t = expr_ty(fcx.ccx.tcx, idx);
if (!type_is_integral(fcx, idx.span, idx_t)) {