Convert match on enums to use trans::adt.

This commit is contained in:
Jed Davis 2013-02-28 11:30:27 -08:00
parent 626ad484fe
commit c4682dcabe

View file

@ -151,6 +151,7 @@ use middle::const_eval;
use middle::borrowck::root_map_key;
use middle::pat_util::*;
use middle::resolve::DefMap;
use middle::trans::adt;
use middle::trans::base::*;
use middle::trans::build::*;
use middle::trans::callee;
@ -191,15 +192,15 @@ pub enum Lit {
// range)
pub enum Opt {
lit(Lit),
var(/* disr val */int, /* variant dids (enm, var) */(def_id, def_id)),
var(/* disr val */int, adt::Repr),
range(@ast::expr, @ast::expr),
vec_len_eq(uint),
vec_len_ge(uint)
}
pub fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
match (*a, *b) {
(lit(a), lit(b)) => {
match (a, b) {
(&lit(a), &lit(b)) => {
match (a, b) {
(UnitLikeStructLit(a), UnitLikeStructLit(b)) => a == b,
_ => {
@ -233,13 +234,13 @@ pub fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
}
}
}
(range(a1, a2), range(b1, b2)) => {
(&range(a1, a2), &range(b1, b2)) => {
const_eval::compare_lit_exprs(tcx, a1, b1) == 0 &&
const_eval::compare_lit_exprs(tcx, a2, b2) == 0
}
(var(a, _), var(b, _)) => a == b,
(vec_len_eq(a), vec_len_eq(b)) => a == b,
(vec_len_ge(a), vec_len_ge(b)) => a == b,
(&var(a, _), &var(b, _)) => a == b,
(&vec_len_eq(a), &vec_len_eq(b)) => a == b,
(&vec_len_ge(a), &vec_len_ge(b)) => a == b,
_ => false
}
}
@ -267,8 +268,8 @@ pub fn trans_opt(bcx: block, o: &Opt) -> opt_result {
let llval = consts::get_const_val(bcx.ccx(), lit_id);
return single_result(rslt(bcx, llval));
}
var(disr_val, _) => {
return single_result(rslt(bcx, C_int(ccx, disr_val)));
var(disr_val, ref repr) => {
return adt::trans_case(bcx, repr, disr_val);
}
range(l1, l2) => {
return range_result(rslt(bcx, consts::const_expr(ccx, l1)),
@ -283,13 +284,16 @@ pub fn trans_opt(bcx: block, o: &Opt) -> opt_result {
}
}
pub fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> Opt {
match tcx.def_map.get(&pat_id) {
pub fn variant_opt(bcx: block, pat_id: ast::node_id)
-> Opt {
let ccx = bcx.ccx();
match ccx.tcx.def_map.get(&pat_id) {
ast::def_variant(enum_id, var_id) => {
let variants = ty::enum_variants(tcx, enum_id);
let variants = ty::enum_variants(ccx.tcx, enum_id);
for vec::each(*variants) |v| {
if var_id == v.id {
return var(v.disr_val, (enum_id, var_id));
return var(v.disr_val,
adt::represent_node(bcx, pat_id))
}
}
::core::util::unreachable();
@ -298,7 +302,7 @@ pub fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> Opt {
return lit(UnitLikeStructLit(pat_id));
}
_ => {
tcx.sess.bug(~"non-variant or struct in variant_opt()");
ccx.sess.bug(~"non-variant or struct in variant_opt()");
}
}
}
@ -505,7 +509,7 @@ pub fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
do enter_match(bcx, tcx.def_map, m, col, val) |p| {
match /*bad*/copy p.node {
ast::pat_enum(_, subpats) => {
if opt_eq(tcx, &variant_opt(tcx, p.id), opt) {
if opt_eq(tcx, &variant_opt(bcx, p.id), opt) {
Some(option::get_or_default(subpats,
vec::from_elem(variant_size,
dummy)))
@ -515,7 +519,7 @@ pub fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
}
ast::pat_ident(_, _, None)
if pat_is_variant_or_struct(tcx.def_map, p) => {
if opt_eq(tcx, &variant_opt(tcx, p.id), opt) {
if opt_eq(tcx, &variant_opt(bcx, p.id), opt) {
Some(~[])
} else {
None
@ -537,7 +541,7 @@ pub fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
if opt_eq(tcx, &range(l1, l2), opt) {Some(~[])} else {None}
}
ast::pat_struct(_, field_pats, _) => {
if opt_eq(tcx, &variant_opt(tcx, p.id), opt) {
if opt_eq(tcx, &variant_opt(bcx, p.id), opt) {
// Look up the struct variant ID.
let struct_id;
match tcx.def_map.get(&p.id) {
@ -762,8 +766,9 @@ pub fn enter_region(bcx: block,
// Returns the options in one column of matches. An option is something that
// needs to be conditionally matched at runtime; for example, the discriminant
// on a set of enum variants or a literal.
pub fn get_options(ccx: @CrateContext, m: &[@Match], col: uint) -> ~[Opt] {
fn add_to_set(tcx: ty::ctxt, set: &DVec<Opt>, val: Opt) {
pub fn get_options(bcx: block, m: &[@Match], col: uint) -> ~[Opt] {
let ccx = bcx.ccx();
fn add_to_set(tcx: ty::ctxt, set: &DVec<Opt>, +val: Opt) {
if set.any(|l| opt_eq(tcx, l, &val)) {return;}
set.push(val);
}
@ -781,7 +786,7 @@ pub fn get_options(ccx: @CrateContext, m: &[@Match], col: uint) -> ~[Opt] {
match ccx.tcx.def_map.find(&cur.id) {
Some(ast::def_variant(*)) => {
add_to_set(ccx.tcx, &found,
variant_opt(ccx.tcx, cur.id));
variant_opt(bcx, cur.id));
}
Some(ast::def_struct(*)) => {
add_to_set(ccx.tcx, &found,
@ -800,7 +805,7 @@ pub fn get_options(ccx: @CrateContext, m: &[@Match], col: uint) -> ~[Opt] {
match ccx.tcx.def_map.find(&cur.id) {
Some(ast::def_variant(*)) => {
add_to_set(ccx.tcx, &found,
variant_opt(ccx.tcx, cur.id));
variant_opt(bcx, cur.id));
}
_ => {}
}
@ -827,34 +832,13 @@ pub struct ExtractedBlock {
}
pub fn extract_variant_args(bcx: block,
pat_id: ast::node_id,
vdefs: (def_id, def_id),
repr: &adt::Repr,
disr_val: int,
val: ValueRef)
-> ExtractedBlock {
let (enm, evar) = vdefs;
-> ExtractedBlock {
let _icx = bcx.insn_ctxt("match::extract_variant_args");
let ccx = *bcx.fcx.ccx;
let enum_ty_substs = match ty::get(node_id_type(bcx, pat_id)).sty {
ty::ty_enum(id, ref substs) => {
assert id == enm;
/*bad*/copy (*substs).tps
}
_ => bcx.sess().bug(~"extract_variant_args: pattern has non-enum type")
};
let mut blobptr = val;
let variants = ty::enum_variants(ccx.tcx, enm);
let size = ty::enum_variant_with_id(ccx.tcx, enm,
evar).args.len();
if size > 0u && (*variants).len() != 1u {
let enumptr =
PointerCast(bcx, val, T_opaque_enum_ptr(ccx));
blobptr = GEPi(bcx, enumptr, [0u, 1u]);
}
let vdefs_tg = enm;
let vdefs_var = evar;
let args = do vec::from_fn(size) |i| {
GEP_enum(bcx, blobptr, vdefs_tg, vdefs_var,
/*bad*/copy enum_ty_substs, i)
let args = do vec::from_fn(adt::num_args(repr, disr_val)) |i| {
adt::trans_GEP(bcx, repr, val, disr_val, i)
};
ExtractedBlock { vals: args, bcx: bcx }
@ -1365,37 +1349,15 @@ pub fn compile_submatch(bcx: block,
}
// Decide what kind of branch we need
let opts = get_options(ccx, m, col);
let opts = get_options(bcx, m, col);
let mut kind = no_branch;
let mut test_val = val;
if opts.len() > 0u {
match opts[0] {
var(_, (enm, _)) => {
let variants = ty::enum_variants(tcx, enm);
if variants.len() == 1 {
kind = single;
} else {
let enumptr =
PointerCast(bcx, val, T_opaque_enum_ptr(ccx));
let discrimptr = GEPi(bcx, enumptr, [0u, 0u]);
assert variants.len() > 1;
let min_discrim = do variants.foldr(0) |&x, y| {
int::min(x.disr_val, y)
};
let max_discrim = do variants.foldr(0) |&x, y| {
int::max(x.disr_val, y)
};
test_val = LoadRangeAssert(bcx, discrimptr,
min_discrim as c_ulonglong,
(max_discrim + 1)
as c_ulonglong,
lib::llvm::True);
kind = switch;
}
var(_, ref repr) => {
let (the_kind, val_opt) = adt::trans_switch(bcx, repr, val);
kind = the_kind;
for val_opt.each |&tval| { test_val = tval; }
}
lit(_) => {
let pty = node_id_type(bcx, pat_id);
@ -1544,11 +1506,12 @@ pub fn compile_submatch(bcx: block,
let mut size = 0u;
let mut unpacked = ~[];
match *opt {
var(_, vdef) => {
let args = extract_variant_args(opt_cx, pat_id, vdef, val);
size = args.vals.len();
unpacked = /*bad*/copy args.vals;
opt_cx = args.bcx;
var(disr_val, ref repr) => {
let ExtractedBlock {vals: argvals, bcx: new_bcx} =
extract_variant_args(opt_cx, repr, disr_val, val);
size = argvals.len();
unpacked = argvals;
opt_cx = new_bcx;
}
vec_len_eq(n) | vec_len_ge(n) => {
let tail = match *opt {
@ -1757,10 +1720,15 @@ pub fn bind_irrefutable_pat(bcx: block,
}
ast::pat_enum(_, sub_pats) => {
match bcx.tcx().def_map.find(&pat.id) {
Some(ast::def_variant(*)) => {
let pat_def = ccx.tcx.def_map.get(&pat.id);
let vdefs = ast_util::variant_def_ids(pat_def);
let args = extract_variant_args(bcx, pat.id, vdefs, val);
Some(ast::def_variant(enum_id, var_id)) => {
let repr = adt::represent_node(bcx, pat.id);
let vinfo = ty::enum_variant_with_id(ccx.tcx,
enum_id,
var_id);
let args = extract_variant_args(bcx,
&repr,
vinfo.disr_val,
val);
for sub_pats.each |sub_pat| {
for vec::eachi(args.vals) |i, argval| {
bcx = bind_irrefutable_pat(bcx,