diff options
| author | Patrick Walton <pcwalton@mimiga.net> | 2012-08-06 17:01:14 -0700 |
|---|---|---|
| committer | Patrick Walton <pcwalton@mimiga.net> | 2012-08-06 17:36:24 -0700 |
| commit | 253dfc338775570a76d6c68bf349b2026e700797 (patch) | |
| tree | 016895b995fc0bce525eae43c83edecae9e9e750 | |
| parent | 5cb3a94bfbafc6548b2dde42beddc903a97c9eb2 (diff) | |
| download | rust-253dfc338775570a76d6c68bf349b2026e700797.tar.gz rust-253dfc338775570a76d6c68bf349b2026e700797.zip | |
rustc: Implement pattern matching for structs
| -rw-r--r-- | src/libsyntax/ast.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/ast_util.rs | 3 | ||||
| -rw-r--r-- | src/libsyntax/fold.rs | 10 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 190 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 18 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 6 | ||||
| -rw-r--r-- | src/rustc/middle/borrowck/gather_loans.rs | 8 | ||||
| -rw-r--r-- | src/rustc/middle/check_alt.rs | 41 | ||||
| -rw-r--r-- | src/rustc/middle/resolve3.rs | 38 | ||||
| -rw-r--r-- | src/rustc/middle/trans/alt.rs | 101 | ||||
| -rw-r--r-- | src/rustc/middle/trans/base.rs | 35 | ||||
| -rw-r--r-- | src/rustc/middle/typeck/check/alt.rs | 80 | ||||
| -rw-r--r-- | src/test/run-pass/struct-pattern-matching.rs | 14 |
13 files changed, 445 insertions, 100 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 8badafa0e80..a325b8c634a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -166,6 +166,7 @@ enum pat_ { pat_enum(@path, option<~[@pat]>), // "none" means a * pattern where // we don't bind the fields to names pat_rec(~[field_pat], bool), + pat_struct(@path, ~[field_pat], bool), pat_tup(~[@pat]), pat_box(@pat), pat_uniq(@pat), diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 4f261afa3de..45556e941d4 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -602,7 +602,8 @@ fn walk_pat(pat: @pat, it: fn(@pat)) { it(pat); match pat.node { pat_ident(_, pth, some(p)) => walk_pat(p, it), - pat_rec(fields, _) => for fields.each |f| { walk_pat(f.pat, it) } + pat_rec(fields, _) | pat_struct(_, fields, _) => + for fields.each |f| { walk_pat(f.pat, it) } pat_enum(_, some(s)) | pat_tup(s) => for s.each |p| { walk_pat(p, it) } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 40c676f8b80..57cebbf044a 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -354,6 +354,16 @@ fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ { } pat_rec(fs, etc) } + pat_struct(pth, fields, etc) => { + let pth_ = fld.fold_path(pth); + let mut fs = ~[]; + for fields.each |f| { + vec::push(fs, + {ident: /* FIXME (#2543) */ copy f.ident, + pat: fld.fold_pat(f.pat)}); + } + pat_struct(pth_, fs, etc) + } pat_tup(elts) => pat_tup(vec::map(elts, |x| fld.fold_pat(x))), pat_box(inner) => pat_box(fld.fold_pat(inner)), pat_uniq(inner) => pat_uniq(fld.fold_pat(inner)), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 932b3c35861..42bdab193be 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -42,14 +42,14 @@ import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute, mac_ellipsis, mac_invoc, mac_invoc_tt, mac_var, matcher, match_nonterminal, match_seq, match_tok, method, mode, mt, mul, mutability, neg, noreturn, not, pat, pat_box, pat_enum, - pat_ident, pat_lit, pat_range, pat_rec, pat_tup, pat_uniq, - pat_wild, path, private, proto, proto_bare, proto_block, - proto_box, proto_uniq, provided, public, pure_fn, purity, - re_anon, re_named, region, rem, required, ret_style, return_val, - self_ty, shl, shr, stmt, stmt_decl, stmt_expr, stmt_semi, - subtract, sty_box, sty_by_ref, sty_region, sty_uniq, sty_value, - token_tree, trait_method, trait_ref, tt_delim, tt_seq, tt_tok, - tt_nonterminal, ty, ty_, ty_bot, ty_box, ty_field, ty_fn, + pat_ident, pat_lit, pat_range, pat_rec, pat_struct, pat_tup, + pat_uniq, pat_wild, path, private, proto, proto_bare, + proto_block, proto_box, proto_uniq, provided, public, pure_fn, + purity, re_anon, re_named, region, rem, required, ret_style, + return_val, self_ty, shl, shr, stmt, stmt_decl, stmt_expr, + stmt_semi, subtract, sty_box, sty_by_ref, sty_region, sty_uniq, + sty_value, token_tree, trait_method, trait_ref, tt_delim, tt_seq, + tt_tok, tt_nonterminal, ty, ty_, ty_bot, ty_box, ty_field, ty_fn, ty_infer, ty_mac, ty_method, ty_nil, ty_param, ty_path, ty_ptr, ty_rec, ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec, ty_fixed_length, unchecked_blk, uniq, unsafe_blk, unsafe_fn, @@ -1640,6 +1640,52 @@ class parser { }; } + fn parse_pat_fields(refutable: bool) -> (~[ast::field_pat], bool) { + let mut fields = ~[]; + let mut etc = false; + let mut first = true; + while self.token != token::RBRACE { + if first { first = false; } + else { self.expect(token::COMMA); } + + if self.token == token::UNDERSCORE { + self.bump(); + if self.token != token::RBRACE { + self.fatal(~"expected `}`, found `" + + token_to_str(self.reader, self.token) + + ~"`"); + } + etc = true; + break; + } + + let lo1 = self.last_span.lo; + let fieldname = if self.look_ahead(1u) == token::COLON { + self.parse_ident() + } else { + self.parse_value_ident() + }; + let hi1 = self.last_span.lo; + let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1), + fieldname); + let mut subpat; + if self.token == token::COLON { + self.bump(); + subpat = self.parse_pat(refutable); + } else { + subpat = @{ + id: self.get_id(), + node: pat_ident(bind_by_implicit_ref, + fieldpath, + none), + span: self.last_span + }; + } + vec::push(fields, {ident: fieldname, pat: subpat}); + } + return (fields, etc); + } + fn parse_pat(refutable: bool) -> @pat { maybe_whole!{self, nt_pat}; @@ -1685,48 +1731,7 @@ class parser { } token::LBRACE => { self.bump(); - let mut fields = ~[]; - let mut etc = false; - let mut first = true; - while self.token != token::RBRACE { - if first { first = false; } - else { self.expect(token::COMMA); } - - if self.token == token::UNDERSCORE { - self.bump(); - if self.token != token::RBRACE { - self.fatal(~"expected `}`, found `" + - token_to_str(self.reader, self.token) + - ~"`"); - } - etc = true; - break; - } - - let lo1 = self.last_span.lo; - let fieldname = if self.look_ahead(1u) == token::COLON { - self.parse_ident() - } else { - self.parse_value_ident() - }; - let hi1 = self.last_span.lo; - let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1), - fieldname); - let mut subpat; - if self.token == token::COLON { - self.bump(); - subpat = self.parse_pat(refutable); - } else { - subpat = @{ - id: self.get_id(), - node: pat_ident(bind_by_implicit_ref, - fieldpath, - none), - span: mk_sp(lo, hi) - }; - } - vec::push(fields, {ident: fieldname, pat: subpat}); - } + let (fields, etc) = self.parse_pat_fields(refutable); hi = self.span.hi; self.bump(); pat = pat_rec(fields, etc); @@ -1771,21 +1776,82 @@ class parser { } else if !is_plain_ident(self.token) { pat = self.parse_enum_variant(refutable); } else { - // this is a plain identifier, like `x` or `x(...)` + let binding_mode; + if self.eat_keyword(~"copy") { + binding_mode = bind_by_value; + } else if refutable { + // XXX: Should be bind_by_value, but that's not + // backward compatible. + binding_mode = bind_by_implicit_ref; + } else { + binding_mode = bind_by_value; + } + + let cannot_be_enum_or_struct; match self.look_ahead(1) { - token::LPAREN | token::LBRACKET | token::LT => { - pat = self.parse_enum_variant(refutable); - } - _ => { - let binding_mode = if refutable { - // XXX: Should be bind_by_value, but that's not - // backward compatible. - bind_by_implicit_ref + token::LPAREN | token::LBRACKET | token::LT | + token::LBRACE => + cannot_be_enum_or_struct = false, + _ => + cannot_be_enum_or_struct = true + } + + if is_plain_ident(self.token) && cannot_be_enum_or_struct { + let name = self.parse_value_path(); + let sub; + if self.eat(token::AT) { + sub = some(self.parse_pat(refutable)); } else { - bind_by_value + sub = none; }; - pat = self.parse_pat_ident(refutable, binding_mode); - } + pat = pat_ident(binding_mode, name, sub); + } else { + let enum_path = self.parse_path_with_tps(true); + match self.token { + token::LBRACE => { + self.bump(); + let (fields, etc) = + self.parse_pat_fields(refutable); + self.bump(); + pat = pat_struct(enum_path, fields, etc); + } + _ => { + let mut args: ~[@pat] = ~[]; + let mut star_pat = false; + match self.token { + token::LPAREN => match self.look_ahead(1u) { + token::BINOP(token::STAR) => { + // This is a "top constructor only" pat + self.bump(); self.bump(); + star_pat = true; + self.expect(token::RPAREN); + } + _ => { + args = self.parse_unspanned_seq( + token::LPAREN, token::RPAREN, + seq_sep_trailing_disallowed + (token::COMMA), + |p| p.parse_pat(refutable)); + } + } + _ => () + } + // at this point, we're not sure whether it's a + // enum or a bind + if star_pat { + pat = pat_enum(enum_path, none); + } + else if vec::is_empty(args) && + vec::len(enum_path.idents) == 1u { + pat = pat_ident(binding_mode, + enum_path, + none); + } + else { + pat = pat_enum(enum_path, some(args)); + } + } + } } } hi = self.span.hi; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index cb68ca2dd27..3fd30eb30a9 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1375,6 +1375,24 @@ fn print_pat(s: ps, &&pat: @ast::pat) { } word(s.s, ~"}"); } + ast::pat_struct(path, fields, etc) => { + print_path(s, path, true); + word(s.s, ~"{"); + fn print_field(s: ps, f: ast::field_pat) { + cbox(s, indent_unit); + word(s.s, *f.ident); + word_space(s, ~":"); + print_pat(s, f.pat); + end(s); + } + fn get_span(f: ast::field_pat) -> codemap::span { return f.pat.span; } + commasep_cmnt(s, consistent, fields, print_field, get_span); + if etc { + if vec::len(fields) != 0u { word_space(s, ~","); } + word(s.s, ~"_"); + } + word(s.s, ~"}"); + } ast::pat_tup(elts) => { popen(s); commasep(s, inconsistent, elts, print_pat); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 7df0fc739ad..c988c89f8c2 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -222,6 +222,12 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) { pat_rec(fields, _) => for fields.each |f| { v.visit_pat(f.pat, e, v) } + pat_struct(path, fields, _) => { + visit_path(path, e, v); + for fields.each |f| { + v.visit_pat(f.pat, e, v); + } + } pat_tup(elts) => for elts.each |elt| { v.visit_pat(elt, e, v) } diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs index 86455652faa..79696becfeb 100644 --- a/src/rustc/middle/borrowck/gather_loans.rs +++ b/src/rustc/middle/borrowck/gather_loans.rs @@ -513,6 +513,14 @@ impl methods for gather_loan_ctxt { } } + ast::pat_struct(_, field_pats, _) => { + // {f1: p1, ..., fN: pN} + for field_pats.each |fp| { + let cmt_field = self.bccx.cat_field(fp.pat, cmt, fp.ident); + self.gather_pat(cmt_field, fp.pat, arm_id, alt_id); + } + } + ast::pat_tup(subpats) => { // (p1, ..., pN) for subpats.each |subpat| { diff --git a/src/rustc/middle/check_alt.rs b/src/rustc/middle/check_alt.rs index 7ccccbbcc07..6b135e2e256 100644 --- a/src/rustc/middle/check_alt.rs +++ b/src/rustc/middle/check_alt.rs @@ -212,7 +212,8 @@ 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_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) | + pat_struct(*) => { some(single) } } @@ -234,7 +235,8 @@ fn is_wild(tcx: ty::ctxt, p: @pat) -> bool { fn missing_ctor(tcx: ty::ctxt, m: matrix, left_ty: ty::t) -> option<ctor> { match ty::get(left_ty).struct { - ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_tup(_) | ty::ty_rec(_) => { + ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_tup(_) | ty::ty_rec(_) | + ty::ty_class(*) => { for m.each |r| { if !is_wild(tcx, r[0]) { return none; } } @@ -286,6 +288,7 @@ fn ctor_arity(tcx: ty::ctxt, ctor: ctor, ty: ty::t) -> uint { some(v) => v.args.len() } } + ty::ty_class(cid, _) => ty::lookup_class_fields(tcx, cid).len(), _ => 0u } } @@ -327,7 +330,29 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint, }; let args = vec::map(ty_flds, |ty_f| { match vec::find(flds, |f| f.ident == ty_f.ident ) { - some(f) => f.pat, _ => wild() + some(f) => f.pat, + _ => wild() + } + }); + 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).struct { + ty::ty_class(cid, substs) => { + 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))) @@ -377,7 +402,9 @@ fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) { fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool { match tcx.def_map.find(pat.id) { some(def_variant(enum_id, var_id)) => { - if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u { return true; } + if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u { + return true; + } } _ => () } @@ -394,6 +421,12 @@ fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool { } false } + pat_struct(_, fields, _) => { + for fields.each |it| { + if is_refutable(tcx, it.pat) { return true; } + } + false + } pat_tup(elts) => { for elts.each |elt| { if is_refutable(tcx, elt) { return true; } } false diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs index f87385664cc..cf8d1f348c9 100644 --- a/src/rustc/middle/resolve3.rs +++ b/src/rustc/middle/resolve3.rs @@ -15,8 +15,8 @@ import syntax::ast::{bound_trait, binding_mode, import syntax::ast::{class_member, class_method, crate, crate_num, decl_item}; import syntax::ast::{def, def_arg, def_binding, def_class, def_const, def_fn}; import syntax::ast::{def_foreign_mod, def_id, def_local, def_mod}; -import syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param, - def_typaram_binder}; +import syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param}; +import syntax::ast::{def_typaram_binder}; import syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op}; import syntax::ast::{expr_binary, expr_cast, expr_field, expr_fn}; import syntax::ast::{expr_fn_block, expr_index, expr_path}; @@ -30,13 +30,13 @@ import syntax::ast::{instance_var, item, item_class, item_const, item_enum}; import syntax::ast::{item_fn, item_mac, item_foreign_mod, item_impl}; import syntax::ast::{item_mod, item_trait, item_ty, le, local, local_crate}; import syntax::ast::{lt, method, mul, ne, neg, node_id, pat, pat_enum}; -import syntax::ast::{pat_ident, path, prim_ty, pat_box, pat_uniq, pat_lit}; -import syntax::ast::{pat_range, pat_rec, pat_tup, pat_wild, provided}; -import syntax::ast::{required, rem, self_ty_, shl, stmt_decl, subtract, ty}; -import syntax::ast::{ty_bool, ty_char, ty_f, ty_f32, ty_f64, ty_float, ty_i}; -import syntax::ast::{ty_i16, ty_i32, ty_i64, ty_i8, ty_int, ty_param}; -import syntax::ast::{ty_path, ty_str, ty_u, ty_u16, ty_u32, ty_u64, ty_u8}; -import syntax::ast::{ty_uint, variant, view_item, view_item_export}; +import syntax::ast::{pat_ident, pat_struct, path, prim_ty, pat_box, pat_uniq}; +import syntax::ast::{pat_lit, pat_range, pat_rec, pat_tup, pat_wild}; +import syntax::ast::{provided, required, rem, self_ty_, shl, stmt_decl}; +import syntax::ast::{subtract, ty, ty_bool, ty_char, ty_f, ty_f32, ty_f64}; +import syntax::ast::{ty_float, ty_i, ty_i16, ty_i32, ty_i64, ty_i8, ty_int}; +import syntax::ast::{ty_param, ty_path, ty_str, ty_u, ty_u16, ty_u32, ty_u64}; +import syntax::ast::{ty_u8, ty_uint, variant, view_item, view_item_export}; import syntax::ast::{view_item_import, view_item_use, view_path_glob}; import syntax::ast::{view_path_list, view_path_simple}; import syntax::ast_util::{def_id_of_def, dummy_sp, local_def, new_def_hash}; @@ -4017,6 +4017,26 @@ class Resolver { self.resolve_expr(last_expr, visitor); } + pat_struct(path, _, _) => { + match self.resolve_path(path, TypeNS, false, visitor) { + some(definition @ def_ty(class_id)) + if self.structs.contains_key(class_id) => { + let has_constructor = self.structs.get(class_id); + let class_def = def_class(class_id, + has_constructor); + self.record_def(pattern.id, class_def); + } + _ => { + self.session.span_err(path.span, + fmt!("`%s` does not name a \ + structure", + connect(path.idents.map + (|x| *x), + ~"::"))); + } + } + } + _ => { // Nothing to do. } diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs index 27efab7de14..6e3034f26ef 100644 --- a/src/rustc/middle/trans/alt.rs +++ b/src/rustc/middle/trans/alt.rs @@ -187,7 +187,8 @@ fn enter_default(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef) do enter_match(bcx, dm, m, col, val) |p| { match p.node { - ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) => some(~[]), + ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) | + ast::pat_struct(*) => some(~[]), ast::pat_ident(_, _, none) if !pat_is_variant(dm, p) => some(~[]), _ => none } @@ -221,12 +222,12 @@ fn enter_opt(bcx: block, m: match_, opt: opt, col: uint, } } -fn enter_rec(bcx: block, dm: DefMap, m: match_, col: uint, - fields: ~[ast::ident], val: ValueRef) -> match_ { +fn enter_rec_or_struct(bcx: block, dm: DefMap, m: match_, col: uint, + fields: ~[ast::ident], val: ValueRef) -> match_ { let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()}; do enter_match(bcx, dm, m, col, val) |p| { match p.node { - ast::pat_rec(fpats, _) => { + ast::pat_rec(fpats, _) | ast::pat_struct(_, fpats, _) => { let mut pats = ~[]; for vec::each(fields) |fname| { let mut pat = dummy; @@ -344,6 +345,23 @@ fn collect_record_fields(m: match_, col: uint) -> ~[ast::ident] { return fields; } +fn collect_struct_fields(m: match_, col: uint) -> ~[ast::ident] { + let mut fields: ~[ast::ident] = ~[]; + for vec::each(m) |br| { + match br.pats[col].node { + ast::pat_struct(_, fs, _) => { + for vec::each(fs) |f| { + if !vec::any(fields, |x| str::eq(f.ident, x)) { + vec::push(fields, f.ident); + } + } + } + _ => () + } + } + return fields; +} + fn root_pats_as_necessary(bcx: block, m: match_, col: uint, val: ValueRef) { for vec::each(m) |br| { let pat_id = br.pats[col].id; @@ -500,15 +518,56 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef], let rec_fields = collect_record_fields(m, col); // Separate path for extracting and binding record fields - if rec_fields.len() > 0u { + if rec_fields.len() > 0 { let fields = ty::get_fields(node_id_type(bcx, pat_id)); let mut rec_vals = ~[]; for vec::each(rec_fields) |field_name| { let ix = option::get(ty::field_idx(field_name, fields)); vec::push(rec_vals, GEPi(bcx, val, ~[0u, ix])); } - compile_submatch(bcx, enter_rec(bcx, dm, m, col, rec_fields, val), - vec::append(rec_vals, vals_left), chk, exits); + compile_submatch(bcx, + enter_rec_or_struct(bcx, dm, m, col, rec_fields, + val), + vec::append(rec_vals, vals_left), + chk, + exits); + return; + } + + // Separate path for extracting and binding struct fields. + let struct_fields = collect_struct_fields(m, col); + if struct_fields.len() > 0 { + let class_id, class_fields; + match ty::get(node_id_type(bcx, pat_id)).struct { + ty::ty_class(cid, _) => { + class_id = cid; + class_fields = ty::lookup_class_fields(ccx.tcx, class_id); + } + _ => { + ccx.tcx.sess.bug(~"struct pattern didn't resolve to a \ + struct"); + } + } + + // Index the class fields. + let field_map = std::map::box_str_hash(); + for class_fields.eachi |i, class_field| { + field_map.insert(class_field.ident, i); + } + + // Fetch each field. + let mut struct_vals = ~[]; + for struct_fields.each |field_name| { + let index = field_map.get(field_name); + let fldptr = base::get_struct_field(bcx, val, class_id, index); + vec::push(struct_vals, fldptr); + } + compile_submatch(bcx, + enter_rec_or_struct(bcx, dm, m, col, struct_fields, + val), + vec::append(struct_vals, vals_left), + chk, + exits); return; } @@ -877,6 +936,34 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef, bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy); } } + ast::pat_struct(_, fields, _) => { + // Grab the class data that we care about. + let class_fields, class_id; + match ty::get(node_id_type(bcx, pat.id)).struct { + ty::ty_class(cid, substs) => { + class_id = cid; + class_fields = ty::lookup_class_fields(ccx.tcx, class_id); + } + _ => { + ccx.tcx.sess.span_bug(pat.span, ~"struct pattern didn't \ + resolve to a struct"); + } + } + + // Index the class fields. + let field_map = std::map::box_str_hash(); + for class_fields.eachi |i, class_field| { + field_map.insert(class_field.ident, i); + } + + // Fetch each field. + for fields.each |supplied_field| { + let index = field_map.get(supplied_field.ident); + let fldptr = base::get_struct_field(bcx, val, class_id, index); + bcx = bind_irrefutable_pat(bcx, supplied_field.pat, fldptr, + make_copy); + } + } ast::pat_tup(elems) => { let mut i = 0u; for vec::each(elems) |elem| { diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 6e882d71c00..2f090cdab7c 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -3391,6 +3391,18 @@ fn trans_rec(bcx: block, fields: ~[ast::field], return bcx; } +// If the class has a destructor, our GEP is a little more +// complicated. +fn get_struct_field(block_context: block, dest_address: ValueRef, + class_id: ast::def_id, index: uint) -> ValueRef { + if ty::ty_dtor(block_context.tcx(), class_id).is_some() { + return GEPi(block_context, + GEPi(block_context, dest_address, ~[0, 1]), + ~[0, index]); + } + return GEPi(block_context, dest_address, ~[0, index]); +} + fn trans_struct(block_context: block, span: span, fields: ~[ast::field], base: option<@ast::expr>, id: ast::node_id, dest: dest) -> block { @@ -3434,18 +3446,6 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field], } } - // If the class has a destructor, our GEP is a little more - // complicated. - fn get_field(block_context: block, dest_address: ValueRef, - class_id: ast::def_id, index: uint) -> ValueRef { - if ty::ty_dtor(block_context.tcx(), class_id).is_some() { - return GEPi(block_context, - GEPi(block_context, dest_address, ~[0, 1]), - ~[0, index]); - } - return GEPi(block_context, dest_address, ~[0, index]); - } - // Now translate each field. let mut temp_cleanups = ~[]; for fields.each |field| { @@ -3468,7 +3468,8 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field], } } - let dest = get_field(block_context, dest_address, class_id, index); + let dest = get_struct_field(block_context, dest_address, class_id, + index); block_context = trans_expr_save_in(block_context, field.node.expr, @@ -3494,10 +3495,10 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field], if exists { again; } - let lldestfieldvalue = get_field(block_context, - dest_address, - class_id, - i); + let lldestfieldvalue = get_struct_field(block_context, + dest_address, + class_id, + i); let llbasefieldvalue = GEPi(block_context, llbasevalue, ~[0, i]); diff --git a/src/rustc/middle/typeck/check/alt.rs b/src/rustc/middle/typeck/check/alt.rs index 238d5389688..4fb348e598c 100644 --- a/src/rustc/middle/typeck/check/alt.rs +++ b/src/rustc/middle/typeck/check/alt.rs @@ -232,6 +232,86 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { } fcx.write_ty(pat.id, expected); } + ast::pat_struct(path, fields, etc) => { + // Grab the class data that we care about. + let class_fields, class_id, substitutions; + match structure_of(fcx, pat.span, expected) { + ty::ty_class(cid, substs) => { + class_id = cid; + substitutions = substs; + class_fields = ty::lookup_class_fields(tcx, class_id); + } + _ => { + // XXX: This should not be fatal. + tcx.sess.span_fatal(pat.span, + fmt!("mismatched types: expected `%s` \ + but found struct", + fcx.infcx.ty_to_str(expected))); + } + } + + // Check to ensure that the struct is the one specified. + match tcx.def_map.get(pat.id) { + ast::def_class(supplied_def_id, _) + if supplied_def_id == class_id => { + // OK. + } + ast::def_class(*) => { + let name = syntax::print::pprust::path_to_str(path); + tcx.sess.span_err(pat.span, + fmt!("mismatched types: expected `%s` but \ + found `%s`", + fcx.infcx.ty_to_str(expected), + name)); + } + _ => { + tcx.sess.span_bug(pat.span, ~"resolve didn't write in class"); + } + } + + // Index the class fields. + let field_map = std::map::box_str_hash(); + for class_fields.eachi |i, class_field| { + field_map.insert(class_field.ident, i); + } + + // Typecheck each field. + let found_fields = std::map::uint_hash(); + for fields.each |field| { + match field_map.find(field.ident) { + some(index) => { + let class_field = class_fields[index]; + let field_type = ty::lookup_field_type(tcx, + class_id, + class_field.id, + substitutions); + check_pat(pcx, field.pat, field_type); + found_fields.insert(index, ()); + } + none => { + let name = syntax::print::pprust::path_to_str(path); + tcx.sess.span_err(pat.span, + fmt!("struct `%s` does not have a field + named `%s`", name, *field.ident)); + } + } + } + + // Report an error if not all the fields were specified. + if !etc { + for class_fields.eachi |i, field| { + if found_fields.contains_key(i) { + again; + } + tcx.sess.span_err(pat.span, + fmt!("pattern does not mention field `%s`", + *field.ident)); + } + } + + // Finally, write in the type. + fcx.write_ty(pat.id, expected); + } ast::pat_tup(elts) => { let ex_elts = match structure_of(fcx, pat.span, expected) { ty::ty_tup(elts) => elts, diff --git a/src/test/run-pass/struct-pattern-matching.rs b/src/test/run-pass/struct-pattern-matching.rs new file mode 100644 index 00000000000..0a4f3b933c0 --- /dev/null +++ b/src/test/run-pass/struct-pattern-matching.rs @@ -0,0 +1,14 @@ +struct Foo { + x: int; + y: int; +} + +fn main() { + let a = Foo { x: 1, y: 2 }; + match a { + Foo { x: x, y: y } => io::println(fmt!("yes, %d, %d", x, y)) + } +} + + + |
