Skip to content

Commit 57cd6b3

Browse files
committed
rustc: Translate and check exhaustiveness of struct-like enum variant patterns. r=nmatsakis
1 parent 65ee0e1 commit 57cd6b3

File tree

5 files changed

+150
-20
lines changed

5 files changed

+150
-20
lines changed

src/rustc/middle/check_alt.rs

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,14 @@ fn pat_ctor_id(tcx: ty::ctxt, p: @pat) -> Option<ctor> {
234234
pat_range(lo, hi) => {
235235
Some(range(eval_const_expr(tcx, lo), eval_const_expr(tcx, hi)))
236236
}
237-
pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) | pat_region(*) |
238237
pat_struct(*) => {
238+
match tcx.def_map.find(pat.id) {
239+
Some(def_variant(_, id)) => Some(variant(id)),
240+
_ => Some(single)
241+
}
242+
}
243+
pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) |
244+
pat_region(*) => {
239245
Some(single)
240246
}
241247
}
@@ -366,25 +372,44 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint,
366372
Some(vec::append(args, vec::tail(r)))
367373
}
368374
pat_struct(_, flds, _) => {
369-
// Grab the class data that we care about.
370-
let class_fields, class_id;
371-
match ty::get(left_ty).sty {
372-
ty::ty_class(cid, _) => {
373-
class_id = cid;
374-
class_fields = ty::lookup_class_fields(tcx, class_id);
375+
// Is this a struct or an enum variant?
376+
match tcx.def_map.get(r0.id) {
377+
def_variant(_, variant_id) => {
378+
if variant(variant_id) == ctor_id {
379+
// XXX: Is this right? --pcw
380+
let args = flds.map(|ty_f| {
381+
match vec::find(flds, |f| f.ident == ty_f.ident) {
382+
Some(f) => f.pat,
383+
_ => wild()
384+
}
385+
});
386+
Some(vec::append(args, vec::tail(r)))
387+
} else {
388+
None
389+
}
375390
}
376391
_ => {
377-
tcx.sess.span_bug(r0.span, ~"struct pattern didn't resolve \
378-
to a struct");
392+
// Grab the class data that we care about.
393+
let class_fields, class_id;
394+
match ty::get(left_ty).sty {
395+
ty::ty_class(cid, _) => {
396+
class_id = cid;
397+
class_fields = ty::lookup_class_fields(tcx, class_id);
398+
}
399+
_ => {
400+
tcx.sess.span_bug(r0.span, ~"struct pattern didn't \
401+
resolve to a struct");
402+
}
403+
}
404+
let args = vec::map(class_fields, |class_field| {
405+
match vec::find(flds, |f| f.ident == class_field.ident ) {
406+
Some(f) => f.pat,
407+
_ => wild()
408+
}
409+
});
410+
Some(vec::append(args, vec::tail(r)))
379411
}
380412
}
381-
let args = vec::map(class_fields, |class_field| {
382-
match vec::find(flds, |f| f.ident == class_field.ident ) {
383-
Some(f) => f.pat,
384-
_ => wild()
385-
}
386-
});
387-
Some(vec::append(args, vec::tail(r)))
388413
}
389414
pat_tup(args) => Some(vec::append(args, vec::tail(r))),
390415
pat_box(a) | pat_uniq(a) | pat_region(a) =>

src/rustc/middle/pat_util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn pat_id_map(dm: resolve::DefMap, pat: @pat) -> PatIdMap {
2424
fn pat_is_variant(dm: resolve::DefMap, pat: @pat) -> bool {
2525
match pat.node {
2626
pat_enum(_, _) => true,
27-
pat_ident(_, _, None) => match dm.find(pat.id) {
27+
pat_ident(_, _, None) | pat_struct(*) => match dm.find(pat.id) {
2828
Some(def_variant(_, _)) => true,
2929
_ => false
3030
},

src/rustc/middle/trans/alt.rs

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,30 @@ fn enter_default(bcx: block, dm: DefMap, m: &[@Match/&r],
369369
}
370370
}
371371

372+
// <pcwalton> nmatsakis: what does enter_opt do?
373+
// <pcwalton> in trans/alt
374+
// <pcwalton> trans/alt.rs is like stumbling around in a dark cave
375+
// <nmatsakis> pcwalton: the enter family of functions adjust the set of
376+
// patterns as needed
377+
// <nmatsakis> yeah, at some point I kind of achieved some level of
378+
// understanding
379+
// <nmatsakis> anyhow, they adjust the patterns given that something of that
380+
// kind has been found
381+
// <nmatsakis> pcwalton: ok, right, so enter_XXX() adjusts the patterns, as I
382+
// said
383+
// <nmatsakis> enter_match() kind of embodies the generic code
384+
// <nmatsakis> it is provided with a function that tests each pattern to see
385+
// if it might possibly apply and so forth
386+
// <nmatsakis> so, if you have a pattern like {a: _, b: _, _} and one like _
387+
// <nmatsakis> then _ would be expanded to (_, _)
388+
// <nmatsakis> one spot for each of the sub-patterns
389+
// <nmatsakis> enter_opt() is one of the more complex; it covers the fallible
390+
// cases
391+
// <nmatsakis> enter_rec_or_struct() or enter_tuple() are simpler, since they
392+
// are infallible patterns
393+
// <nmatsakis> so all patterns must either be records (resp. tuples) or
394+
// wildcards
395+
372396
fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
373397
variant_size: uint, val: ValueRef)
374398
-> ~[@Match/&r]
@@ -406,6 +430,35 @@ fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
406430
ast::pat_range(l1, l2) => {
407431
if opt_eq(tcx, &range(l1, l2), opt) {Some(~[])} else {None}
408432
}
433+
ast::pat_struct(_, field_pats, _) => {
434+
if opt_eq(tcx, &variant_opt(tcx, p.id), opt) {
435+
// Look up the struct variant ID.
436+
let struct_id;
437+
match tcx.def_map.get(p.id) {
438+
ast::def_variant(_, found_struct_id) => {
439+
struct_id = found_struct_id;
440+
}
441+
_ => {
442+
tcx.sess.span_bug(p.span, ~"expected enum \
443+
variant def");
444+
}
445+
}
446+
447+
// Reorder the patterns into the same order they were
448+
// specified in the struct definition. Also fill in
449+
// unspecified fields with dummy.
450+
let reordered_patterns = dvec::DVec();
451+
for ty::lookup_class_fields(tcx, struct_id).each |field| {
452+
match field_pats.find(|p| p.ident == field.ident) {
453+
None => reordered_patterns.push(dummy),
454+
Some(fp) => reordered_patterns.push(fp.pat)
455+
}
456+
}
457+
Some(dvec::unwrap(move reordered_patterns))
458+
} else {
459+
None
460+
}
461+
}
409462
_ => {
410463
assert_is_binding_or_wild(bcx, p);
411464
Some(vec::from_elem(variant_size, dummy))
@@ -599,12 +652,19 @@ fn extract_variant_args(bcx: block, pat_id: ast::node_id,
599652
return {vals: args, bcx: bcx};
600653
}
601654

602-
fn collect_record_or_struct_fields(m: &[@Match], col: uint) -> ~[ast::ident] {
655+
// NB: This function does not collect fields from struct-like enum variants.
656+
fn collect_record_or_struct_fields(bcx: block, m: &[@Match], col: uint) ->
657+
~[ast::ident] {
603658
let mut fields: ~[ast::ident] = ~[];
604659
for vec::each(m) |br| {
605660
match br.pats[col].node {
606661
ast::pat_rec(fs, _) => extend(&mut fields, fs),
607-
ast::pat_struct(_, fs, _) => extend(&mut fields, fs),
662+
ast::pat_struct(_, fs, _) => {
663+
match ty::get(node_id_type(bcx, br.pats[col].id)).sty {
664+
ty::ty_class(*) => extend(&mut fields, fs),
665+
_ => ()
666+
}
667+
}
608668
_ => ()
609669
}
610670
}
@@ -939,7 +999,7 @@ fn compile_submatch(bcx: block,
939999
9401000
root_pats_as_necessary(bcx, m, col, val);
9411001
942-
let rec_fields = collect_record_or_struct_fields(m, col);
1002+
let rec_fields = collect_record_or_struct_fields(bcx, m, col);
9431003
if rec_fields.len() > 0 {
9441004
let pat_ty = node_id_type(bcx, pat_id);
9451005
do expr::with_field_tys(tcx, pat_ty, None) |_has_dtor, field_tys| {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
enum A {
2+
B { x: Option<int> },
3+
C
4+
}
5+
6+
fn main() {
7+
let x = B { x: Some(3) };
8+
match x { //~ ERROR non-exhaustive patterns
9+
C => {}
10+
B { x: None } => {}
11+
}
12+
}
13+
14+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
enum Foo {
2+
Bar {
3+
x: int,
4+
y: int
5+
},
6+
Baz {
7+
x: float,
8+
y: float
9+
}
10+
}
11+
12+
fn f(x: &Foo) {
13+
match *x {
14+
Baz { x: x, y: y } => {
15+
assert x == 1.0;
16+
assert y == 2.0;
17+
}
18+
Bar { y: y, x: x } => {
19+
assert x == 1;
20+
assert y == 2;
21+
}
22+
}
23+
}
24+
25+
fn main() {
26+
let x = Bar { x: 1, y: 2 };
27+
f(&x);
28+
let y = Baz { x: 1.0, y: 2.0 };
29+
f(&y);
30+
}
31+

0 commit comments

Comments
 (0)