Skip to content

Commit

Permalink
auto merge of #4866 : jld/rust/enum-unitlike, r=graydon
Browse files Browse the repository at this point in the history
If an enum is isomorphic to unit, there's no need to use any bits to
represent it.  The only obvious reason this wasn't the case was because
the enum could be C-like and have a user-specified discriminant -- but
that value is constant, so it doesn't need to be stored.

This change means that all newtype-like enums have the same size (and
layout) as their underlying type, which might be a useful property to
have, at least in terms of making programs' low-level behavior less
surprising.
  • Loading branch information
bors committed Feb 10, 2013
2 parents 9d7014e + 3742b62 commit 0f04df8
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 14 deletions.
11 changes: 7 additions & 4 deletions src/librustc/middle/trans/consts.rs
Expand Up @@ -407,6 +407,10 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
// variant or we wouldn't have gotten here -- the constant
// checker forbids paths that don't map to C-like enum
// variants.
if ty::enum_is_univariant(cx.tcx, enum_did) {
// Univariants have no discriminant field.
C_struct(~[])
} else {
let lldiscrim = base::get_discrim_val(cx, e.span,
enum_did,
variant_did);
Expand All @@ -418,6 +422,7 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
let padding = C_null(T_array(T_i8(), size));
C_struct(~[lldiscrim, padding])
}
}
Some(ast::def_struct(_)) => {
let ety = ty::expr_ty(cx.tcx, e);
let llty = type_of::type_of(cx, ety);
Expand All @@ -442,14 +447,14 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
}
Some(ast::def_variant(tid, vid)) => {
let ety = ty::expr_ty(cx.tcx, e);
let degen = ty::enum_is_univariant(cx.tcx, tid);
let univar = ty::enum_is_univariant(cx.tcx, tid);
let size = machine::static_size_of_enum(cx, ety);

let discrim = base::get_discrim_val(cx, e.span, tid, vid);
let c_args = C_struct(args.map(|a| const_expr(cx, *a)));

// FIXME (#1645): enum body alignment is generaly wrong.
if !degen {
if !univar {
// Pad out the data to the size of its type_of;
// this is necessary if the enum is contained
// within an aggregate (tuple, struct, vector) so
Expand All @@ -464,8 +469,6 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
// without affecting its internal alignment or
// changing the alignment of the enum.
C_struct(~[discrim, C_packed_struct(~[c_args]), padding])
} else if size == 0 {
C_struct(~[discrim])
} else {
C_struct(~[c_args])
}
Expand Down
25 changes: 20 additions & 5 deletions src/librustc/middle/trans/expr.rs
Expand Up @@ -674,12 +674,15 @@ fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
// N-ary variant.
let fn_data = callee::trans_fn_ref(bcx, vid, ref_expr.id);
return fn_data_to_datum(bcx, vid, fn_data, lldest);
} else {
} else if !ty::enum_is_univariant(ccx.tcx, tid) {
// Nullary variant.
let lldiscrimptr = GEPi(bcx, lldest, [0u, 0u]);
let lldiscrim = C_int(bcx.ccx(), variant_info.disr_val);
Store(bcx, lldiscrim, lldiscrimptr);
return bcx;
} else {
// Nullary univariant.
return bcx;
}
}
ast::def_struct(*) => {
Expand Down Expand Up @@ -1591,10 +1594,22 @@ fn trans_imm_cast(bcx: block, expr: @ast::expr,
{in: cast_enum, out: cast_integral} |
{in: cast_enum, out: cast_float} => {
let bcx = bcx;
let llenumty = T_opaque_enum_ptr(ccx);
let av_enum = PointerCast(bcx, llexpr, llenumty);
let lldiscrim_a_ptr = GEPi(bcx, av_enum, [0u, 0u]);
let lldiscrim_a = Load(bcx, lldiscrim_a_ptr);
let in_tid = match ty::get(t_in).sty {
ty::ty_enum(did, _) => did,
_ => ccx.sess.bug(~"enum cast source is not enum")
};
let variants = ty::enum_variants(ccx.tcx, in_tid);
let lldiscrim_a = if variants.len() == 1 {
// Univariants don't have a discriminant field,
// because there's only one value it could have:
C_integral(T_enum_discrim(ccx),
variants[0].disr_val as u64, True)
} else {
let llenumty = T_opaque_enum_ptr(ccx);
let av_enum = PointerCast(bcx, llexpr, llenumty);
let lldiscrim_a_ptr = GEPi(bcx, av_enum, [0u, 0u]);
Load(bcx, lldiscrim_a_ptr)
};
match k_out {
cast_integral => int_cast(bcx, ll_t_out,
val_ty(lldiscrim_a),
Expand Down
7 changes: 2 additions & 5 deletions src/librustc/middle/trans/type_of.rs
Expand Up @@ -242,14 +242,11 @@ pub fn fill_type_of_enum(cx: @crate_ctxt, did: ast::def_id, t: ty::t,
debug!("type_of_enum %?: %?", t, ty::get(t));

let lltys = {
let degen = ty::enum_is_univariant(cx.tcx, did);
let univar = ty::enum_is_univariant(cx.tcx, did);
let size = machine::static_size_of_enum(cx, t);
if !degen {
if !univar {
~[T_enum_discrim(cx), T_array(T_i8(), size)]
}
else if size == 0u {
~[T_enum_discrim(cx)]
}
else {
~[T_array(T_i8(), size)]
}
Expand Down

0 comments on commit 0f04df8

Please sign in to comment.