Convert const ADT construction to trans::adt.

Also converts const cast-from-enum, because it used the same routine to
get the discriminant as what's renovated to construct the enums.

Also fixes ICE on struct-like variants as consts, and provides a slightly
less bad ICE for functional-update-like struct expressions in consts.
This commit is contained in:
Jed Davis 2013-02-24 14:38:59 -08:00
parent 2a028c5ab8
commit bb689c09f5
2 changed files with 45 additions and 114 deletions

View File

@ -828,30 +828,6 @@ pub fn trans_external_path(ccx: @CrateContext, did: ast::def_id, t: ty::t)
};
}
pub fn get_discrim_val(cx: @CrateContext, span: span, enum_did: ast::def_id,
variant_did: ast::def_id) -> ValueRef {
// Can't use `discrims` from the crate context here because
// those discriminants have an extra level of indirection,
// and there's no LLVM constant load instruction.
let mut lldiscrim_opt = None;
for ty::enum_variants(cx.tcx, enum_did).each |variant_info| {
if variant_info.id == variant_did {
lldiscrim_opt = Some(C_int(cx,
variant_info.disr_val));
break;
}
}
match lldiscrim_opt {
None => {
cx.tcx.sess.span_bug(span, ~"didn't find discriminant?!");
}
Some(found_lldiscrim) => {
found_lldiscrim
}
}
}
pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef]) -> block {
let _icx = bcx.insn_ctxt("invoke_");
if bcx.unreachable { return bcx; }

View File

@ -12,6 +12,7 @@ use core::prelude::*;
use lib::llvm::{llvm, ValueRef, TypeRef, Bool, True, False};
use middle::const_eval;
use middle::trans::adt;
use middle::trans::base;
use middle::trans::base::get_insn_ctxt;
use middle::trans::common::*;
@ -328,24 +329,8 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
}
(expr::cast_enum, expr::cast_integral) |
(expr::cast_enum, expr::cast_float) => {
let def = ty::resolve_expr(cx.tcx, base);
let (enum_did, variant_did) = match def {
ast::def_variant(enum_did, variant_did) => {
(enum_did, variant_did)
}
_ => cx.sess.bug(~"enum cast source is not enum")
};
// Note that we know this is a C-like (nullary) enum
// variant or we wouldn't have gotten here
let variants = ty::enum_variants(cx.tcx, enum_did);
let iv = if variants.len() == 1 {
// Univariants don't have a discriminant field,
// because there's only one value it could have:
C_integral(T_i64(),
variants[0].disr_val as u64, True)
} else {
base::get_discrim_val(cx, e.span, enum_did, variant_did)
};
let repr = adt::represent_type(cx, basety);
let iv = C_int(cx, adt::const_get_discrim(cx, &repr, v));
let ety_cast = expr::cast_type_kind(ety);
match ety_cast {
expr::cast_integral => {
@ -373,18 +358,22 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
gv
}
ast::expr_tup(es) => {
C_struct(es.map(|e| const_expr(cx, *e)))
let ety = ty::expr_ty(cx.tcx, e);
let repr = adt::represent_type(cx, ety);
adt::trans_const(cx, &repr, 0, es.map(|e| const_expr(cx, *e)))
}
ast::expr_rec(ref fs, None) => {
C_struct([C_struct(
(*fs).map(|f| const_expr(cx, f.node.expr)))])
}
ast::expr_struct(_, ref fs, _) => {
let ety = ty::expr_ty(cx.tcx, e);
let cs = do expr::with_field_tys(cx.tcx,
ety,
None) |_hd, field_tys| {
field_tys.map(|field_ty| {
let repr = adt::represent_type(cx, ety);
adt::trans_const(cx, &repr, 0,
fs.map(|f| const_expr(cx, f.node.expr)))
}
ast::expr_struct(_, ref fs, None) => {
let ety = ty::expr_ty(cx.tcx, e);
let repr = adt::represent_type(cx, ety);
do expr::with_field_tys(cx.tcx, ety, Some(e.id))
|discr, field_tys| {
let cs = field_tys.map(|field_ty| {
match fs.find(|f| field_ty.ident == f.node.ident) {
Some(ref f) => const_expr(cx, (*f).node.expr),
None => {
@ -392,9 +381,9 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
e.span, ~"missing struct field");
}
}
})
};
C_struct([C_struct(cs)])
});
adt::trans_const(cx, &repr, discr, cs)
}
}
ast::expr_vec(es, ast::m_imm) => {
let (v, _, _) = const_vec(cx, e, es);
@ -452,25 +441,12 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
get_const_val(cx, def_id)
}
Some(ast::def_variant(enum_did, variant_did)) => {
// Note that we know this is a C-like (nullary) enum
// 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);
// However, we still have to pad it out to the
// size of the full enum; see the expr_call case,
// below.
let ety = ty::expr_ty(cx.tcx, e);
let size = machine::static_size_of_enum(cx, ety);
let padding = C_null(T_array(T_i8(), size));
C_struct(~[lldiscrim, padding])
}
let repr = adt::represent_type(cx, ety);
let vinfo = ty::enum_variant_with_id(cx.tcx,
enum_did,
variant_did);
adt::trans_const(cx, &repr, vinfo.disr_val, [])
}
Some(ast::def_struct(_)) => {
let ety = ty::expr_ty(cx.tcx, e);
@ -478,52 +454,31 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
C_null(llty)
}
_ => {
cx.sess.span_bug(e.span,
~"expected a const, fn, or variant def")
cx.sess.span_bug(e.span, ~"expected a const, fn, \
struct, or variant def")
}
}
}
ast::expr_call(callee, args, _) => {
match cx.tcx.def_map.find(&callee.id) {
Some(ast::def_struct(def_id)) => {
let llstructbody =
C_struct(args.map(|a| const_expr(cx, *a)));
if ty::ty_dtor(cx.tcx, def_id).is_present() {
C_struct(~[ llstructbody, C_u8(0) ])
} else {
C_struct(~[ llstructbody ])
}
}
Some(ast::def_variant(tid, vid)) => {
let ety = ty::expr_ty(cx.tcx, e);
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 !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
// that the next element is at the right offset.
let actual_size =
machine::llsize_of_real(cx, llvm::LLVMTypeOf(c_args));
let padding =
C_null(T_array(T_i8(), size - actual_size));
// A packed_struct has an alignment of 1; thus,
// wrapping one around c_args will misalign it the
// same way we normally misalign enum bodies
// without affecting its internal alignment or
// changing the alignment of the enum.
C_struct(~[discrim, C_packed_struct(~[c_args]), padding])
} else {
C_struct(~[c_args])
}
}
_ => cx.sess.span_bug(e.span, ~"expected a struct def")
}
match cx.tcx.def_map.find(&callee.id) {
Some(ast::def_struct(_)) => {
let ety = ty::expr_ty(cx.tcx, e);
let repr = adt::represent_type(cx, ety);
adt::trans_const(cx, &repr, 0,
args.map(|a| const_expr(cx, *a)))
}
Some(ast::def_variant(enum_did, variant_did)) => {
let ety = ty::expr_ty(cx.tcx, e);
let repr = adt::represent_type(cx, ety);
let vinfo = ty::enum_variant_with_id(cx.tcx,
enum_did,
variant_did);
adt::trans_const(cx, &repr, vinfo.disr_val,
args.map(|a| const_expr(cx, *a)))
}
_ => cx.sess.span_bug(e.span, ~"expected a struct or \
variant def")
}
}
ast::expr_paren(e) => { return const_expr(cx, e); }
_ => cx.sess.span_bug(e.span,