From 3742b62f64c8b2a01457b70ee37d3dc167a86a07 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Mon, 7 Jan 2013 03:42:49 -0800 Subject: [PATCH] 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. --- src/librustc/middle/trans/consts.rs | 11 +++++++---- src/librustc/middle/trans/expr.rs | 25 ++++++++++++++++++++----- src/librustc/middle/trans/type_of.rs | 7 ++----- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index bcf796caa7a4a..d86d4c97c3bed 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -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); @@ -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); @@ -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 @@ -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]) } diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index cab2adc43faa6..30ba0e7feeec8 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -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(*) => { @@ -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), diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 84a19cd4c0477..972f702c18a81 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -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)] }