rustc: Translate and check exhaustiveness of struct-like enum variant patterns. r=nmatsakis

This commit is contained in:
Patrick Walton 2012-10-24 18:47:59 -07:00
parent 65ee0e1ded
commit 57cd6b3e3f
5 changed files with 150 additions and 20 deletions

View File

@ -234,8 +234,14 @@ fn pat_ctor_id(tcx: ty::ctxt, p: @pat) -> Option<ctor> {
pat_range(lo, hi) => {
Some(range(eval_const_expr(tcx, lo), eval_const_expr(tcx, hi)))
}
pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) | pat_region(*) |
pat_struct(*) => {
match tcx.def_map.find(pat.id) {
Some(def_variant(_, id)) => Some(variant(id)),
_ => Some(single)
}
}
pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) |
pat_region(*) => {
Some(single)
}
}
@ -366,25 +372,44 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint,
Some(vec::append(args, vec::tail(r)))
}
pat_struct(_, flds, _) => {
// Grab the class data that we care about.
let class_fields, class_id;
match ty::get(left_ty).sty {
ty::ty_class(cid, _) => {
class_id = cid;
class_fields = ty::lookup_class_fields(tcx, class_id);
// Is this a struct or an enum variant?
match tcx.def_map.get(r0.id) {
def_variant(_, variant_id) => {
if variant(variant_id) == ctor_id {
// XXX: Is this right? --pcw
let args = flds.map(|ty_f| {
match vec::find(flds, |f| f.ident == ty_f.ident) {
Some(f) => f.pat,
_ => wild()
}
});
Some(vec::append(args, vec::tail(r)))
} else {
None
}
}
_ => {
tcx.sess.span_bug(r0.span, ~"struct pattern didn't resolve \
to a struct");
// Grab the class data that we care about.
let class_fields, class_id;
match ty::get(left_ty).sty {
ty::ty_class(cid, _) => {
class_id = cid;
class_fields = ty::lookup_class_fields(tcx, class_id);
}
_ => {
tcx.sess.span_bug(r0.span, ~"struct pattern didn't \
resolve to a struct");
}
}
let args = vec::map(class_fields, |class_field| {
match vec::find(flds, |f| f.ident == class_field.ident ) {
Some(f) => f.pat,
_ => wild()
}
});
Some(vec::append(args, vec::tail(r)))
}
}
let args = vec::map(class_fields, |class_field| {
match vec::find(flds, |f| f.ident == class_field.ident ) {
Some(f) => f.pat,
_ => wild()
}
});
Some(vec::append(args, vec::tail(r)))
}
pat_tup(args) => Some(vec::append(args, vec::tail(r))),
pat_box(a) | pat_uniq(a) | pat_region(a) =>

View File

@ -24,7 +24,7 @@ fn pat_id_map(dm: resolve::DefMap, pat: @pat) -> PatIdMap {
fn pat_is_variant(dm: resolve::DefMap, pat: @pat) -> bool {
match pat.node {
pat_enum(_, _) => true,
pat_ident(_, _, None) => match dm.find(pat.id) {
pat_ident(_, _, None) | pat_struct(*) => match dm.find(pat.id) {
Some(def_variant(_, _)) => true,
_ => false
},

View File

@ -369,6 +369,30 @@ fn enter_default(bcx: block, dm: DefMap, m: &[@Match/&r],
}
}
// <pcwalton> nmatsakis: what does enter_opt do?
// <pcwalton> in trans/alt
// <pcwalton> trans/alt.rs is like stumbling around in a dark cave
// <nmatsakis> pcwalton: the enter family of functions adjust the set of
// patterns as needed
// <nmatsakis> yeah, at some point I kind of achieved some level of
// understanding
// <nmatsakis> anyhow, they adjust the patterns given that something of that
// kind has been found
// <nmatsakis> pcwalton: ok, right, so enter_XXX() adjusts the patterns, as I
// said
// <nmatsakis> enter_match() kind of embodies the generic code
// <nmatsakis> it is provided with a function that tests each pattern to see
// if it might possibly apply and so forth
// <nmatsakis> so, if you have a pattern like {a: _, b: _, _} and one like _
// <nmatsakis> then _ would be expanded to (_, _)
// <nmatsakis> one spot for each of the sub-patterns
// <nmatsakis> enter_opt() is one of the more complex; it covers the fallible
// cases
// <nmatsakis> enter_rec_or_struct() or enter_tuple() are simpler, since they
// are infallible patterns
// <nmatsakis> so all patterns must either be records (resp. tuples) or
// wildcards
fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
variant_size: uint, val: ValueRef)
-> ~[@Match/&r]
@ -406,6 +430,35 @@ fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
ast::pat_range(l1, l2) => {
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) {
// Look up the struct variant ID.
let struct_id;
match tcx.def_map.get(p.id) {
ast::def_variant(_, found_struct_id) => {
struct_id = found_struct_id;
}
_ => {
tcx.sess.span_bug(p.span, ~"expected enum \
variant def");
}
}
// Reorder the patterns into the same order they were
// specified in the struct definition. Also fill in
// unspecified fields with dummy.
let reordered_patterns = dvec::DVec();
for ty::lookup_class_fields(tcx, struct_id).each |field| {
match field_pats.find(|p| p.ident == field.ident) {
None => reordered_patterns.push(dummy),
Some(fp) => reordered_patterns.push(fp.pat)
}
}
Some(dvec::unwrap(move reordered_patterns))
} else {
None
}
}
_ => {
assert_is_binding_or_wild(bcx, p);
Some(vec::from_elem(variant_size, dummy))
@ -599,12 +652,19 @@ fn extract_variant_args(bcx: block, pat_id: ast::node_id,
return {vals: args, bcx: bcx};
}
fn collect_record_or_struct_fields(m: &[@Match], col: uint) -> ~[ast::ident] {
// NB: This function does not collect fields from struct-like enum variants.
fn collect_record_or_struct_fields(bcx: block, m: &[@Match], col: uint) ->
~[ast::ident] {
let mut fields: ~[ast::ident] = ~[];
for vec::each(m) |br| {
match br.pats[col].node {
ast::pat_rec(fs, _) => extend(&mut fields, fs),
ast::pat_struct(_, fs, _) => extend(&mut fields, fs),
ast::pat_struct(_, fs, _) => {
match ty::get(node_id_type(bcx, br.pats[col].id)).sty {
ty::ty_class(*) => extend(&mut fields, fs),
_ => ()
}
}
_ => ()
}
}
@ -939,7 +999,7 @@ fn compile_submatch(bcx: block,
root_pats_as_necessary(bcx, m, col, val);
let rec_fields = collect_record_or_struct_fields(m, col);
let rec_fields = collect_record_or_struct_fields(bcx, m, col);
if rec_fields.len() > 0 {
let pat_ty = node_id_type(bcx, pat_id);
do expr::with_field_tys(tcx, pat_ty, None) |_has_dtor, field_tys| {

View File

@ -0,0 +1,14 @@
enum A {
B { x: Option<int> },
C
}
fn main() {
let x = B { x: Some(3) };
match x { //~ ERROR non-exhaustive patterns
C => {}
B { x: None } => {}
}
}

View File

@ -0,0 +1,31 @@
enum Foo {
Bar {
x: int,
y: int
},
Baz {
x: float,
y: float
}
}
fn f(x: &Foo) {
match *x {
Baz { x: x, y: y } => {
assert x == 1.0;
assert y == 2.0;
}
Bar { y: y, x: x } => {
assert x == 1;
assert y == 2;
}
}
}
fn main() {
let x = Bar { x: 1, y: 2 };
f(&x);
let y = Baz { x: 1.0, y: 2.0 };
f(&y);
}