Skip to content

Commit 3742b62

Browse files
committed
Omit discriminant from nullary univariant enums.
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.
1 parent 9d7014e commit 3742b62

File tree

3 files changed

+29
-14
lines changed

3 files changed

+29
-14
lines changed

src/librustc/middle/trans/consts.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,10 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
407407
// variant or we wouldn't have gotten here -- the constant
408408
// checker forbids paths that don't map to C-like enum
409409
// variants.
410+
if ty::enum_is_univariant(cx.tcx, enum_did) {
411+
// Univariants have no discriminant field.
412+
C_struct(~[])
413+
} else {
410414
let lldiscrim = base::get_discrim_val(cx, e.span,
411415
enum_did,
412416
variant_did);
@@ -418,6 +422,7 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
418422
let padding = C_null(T_array(T_i8(), size));
419423
C_struct(~[lldiscrim, padding])
420424
}
425+
}
421426
Some(ast::def_struct(_)) => {
422427
let ety = ty::expr_ty(cx.tcx, e);
423428
let llty = type_of::type_of(cx, ety);
@@ -442,14 +447,14 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
442447
}
443448
Some(ast::def_variant(tid, vid)) => {
444449
let ety = ty::expr_ty(cx.tcx, e);
445-
let degen = ty::enum_is_univariant(cx.tcx, tid);
450+
let univar = ty::enum_is_univariant(cx.tcx, tid);
446451
let size = machine::static_size_of_enum(cx, ety);
447452

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

451456
// FIXME (#1645): enum body alignment is generaly wrong.
452-
if !degen {
457+
if !univar {
453458
// Pad out the data to the size of its type_of;
454459
// this is necessary if the enum is contained
455460
// within an aggregate (tuple, struct, vector) so
@@ -464,8 +469,6 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
464469
// without affecting its internal alignment or
465470
// changing the alignment of the enum.
466471
C_struct(~[discrim, C_packed_struct(~[c_args]), padding])
467-
} else if size == 0 {
468-
C_struct(~[discrim])
469472
} else {
470473
C_struct(~[c_args])
471474
}

src/librustc/middle/trans/expr.rs

+20-5
Original file line numberDiff line numberDiff line change
@@ -674,12 +674,15 @@ fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
674674
// N-ary variant.
675675
let fn_data = callee::trans_fn_ref(bcx, vid, ref_expr.id);
676676
return fn_data_to_datum(bcx, vid, fn_data, lldest);
677-
} else {
677+
} else if !ty::enum_is_univariant(ccx.tcx, tid) {
678678
// Nullary variant.
679679
let lldiscrimptr = GEPi(bcx, lldest, [0u, 0u]);
680680
let lldiscrim = C_int(bcx.ccx(), variant_info.disr_val);
681681
Store(bcx, lldiscrim, lldiscrimptr);
682682
return bcx;
683+
} else {
684+
// Nullary univariant.
685+
return bcx;
683686
}
684687
}
685688
ast::def_struct(*) => {
@@ -1591,10 +1594,22 @@ fn trans_imm_cast(bcx: block, expr: @ast::expr,
15911594
{in: cast_enum, out: cast_integral} |
15921595
{in: cast_enum, out: cast_float} => {
15931596
let bcx = bcx;
1594-
let llenumty = T_opaque_enum_ptr(ccx);
1595-
let av_enum = PointerCast(bcx, llexpr, llenumty);
1596-
let lldiscrim_a_ptr = GEPi(bcx, av_enum, [0u, 0u]);
1597-
let lldiscrim_a = Load(bcx, lldiscrim_a_ptr);
1597+
let in_tid = match ty::get(t_in).sty {
1598+
ty::ty_enum(did, _) => did,
1599+
_ => ccx.sess.bug(~"enum cast source is not enum")
1600+
};
1601+
let variants = ty::enum_variants(ccx.tcx, in_tid);
1602+
let lldiscrim_a = if variants.len() == 1 {
1603+
// Univariants don't have a discriminant field,
1604+
// because there's only one value it could have:
1605+
C_integral(T_enum_discrim(ccx),
1606+
variants[0].disr_val as u64, True)
1607+
} else {
1608+
let llenumty = T_opaque_enum_ptr(ccx);
1609+
let av_enum = PointerCast(bcx, llexpr, llenumty);
1610+
let lldiscrim_a_ptr = GEPi(bcx, av_enum, [0u, 0u]);
1611+
Load(bcx, lldiscrim_a_ptr)
1612+
};
15981613
match k_out {
15991614
cast_integral => int_cast(bcx, ll_t_out,
16001615
val_ty(lldiscrim_a),

src/librustc/middle/trans/type_of.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -242,14 +242,11 @@ pub fn fill_type_of_enum(cx: @crate_ctxt, did: ast::def_id, t: ty::t,
242242
debug!("type_of_enum %?: %?", t, ty::get(t));
243243

244244
let lltys = {
245-
let degen = ty::enum_is_univariant(cx.tcx, did);
245+
let univar = ty::enum_is_univariant(cx.tcx, did);
246246
let size = machine::static_size_of_enum(cx, t);
247-
if !degen {
247+
if !univar {
248248
~[T_enum_discrim(cx), T_array(T_i8(), size)]
249249
}
250-
else if size == 0u {
251-
~[T_enum_discrim(cx)]
252-
}
253250
else {
254251
~[T_array(T_i8(), size)]
255252
}

0 commit comments

Comments
 (0)