diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index 6742c00ccfe..e264b887c38 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -297,6 +297,9 @@ fn arm_defnums(&ast::arm arm) -> vec[node_id] { case (ast::pat_tag(_, ?children)) { for (@ast::pat child in children) { walk_pat(found, child); } } + case (ast::pat_rec(?fields, _)) { + for (ast::field_pat f in fields) { walk_pat(found, f.pat); } + } case (_) { } } } diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index 5de67552e7f..2419824cdff 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -313,6 +313,9 @@ fn resolve_names(&@env e, &@ast::crate c) { } } } + case (ast::pat_rec(?fields, _)) { + for (ast::field_pat f in fields) { walk_pat(e, sc, f.pat); } + } case (_) { } } } @@ -707,6 +710,12 @@ fn lookup_in_pat(&ident name, &ast::pat pat) -> option::t[def] { if (!option::is_none(found)) { ret found; } } } + case (ast::pat_rec(?fields, _)) { + for (ast::field_pat f in fields) { + auto found = lookup_in_pat(name, *f.pat); + if (!option::is_none(found)) { ret found; } + } + } } ret none[def]; } @@ -1261,6 +1270,9 @@ fn check_arm(@env e, &ast::arm a, &() x, &vt[()] v) { case (ast::pat_tag(_, ?children)) { for (@ast::pat child in children) { walk_pat(ch, child); } } + case (ast::pat_rec(?fields, _)) { + for (ast::field_pat f in fields) { walk_pat(ch, f.pat); } + } case (_) { } } } diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 461a42ebc42..050cd59e1d4 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -4839,6 +4839,23 @@ fn trans_pat_match(&@block_ctxt cx, &@ast::pat pat, ValueRef llval, } ret rslt(matched_cx, llval); } + case (ast::pat_rec(?field_pats, _)) { + auto bcx = cx; + auto ccx = cx.fcx.lcx.ccx; + auto rec_ty = pat_ty(ccx.tcx, pat); + auto fields = alt (ty::struct(ccx.tcx, rec_ty)) { + ty::ty_rec(?fields) { fields } + }; + for (ast::field_pat f in field_pats) { + let uint ix = ty::field_idx(ccx.sess, f.pat.span, + f.ident, fields); + auto r = GEP_tup_like(bcx, rec_ty, llval, ~[0, ix as int]); + auto v = load_if_immediate(r.bcx, r.val, + pat_ty(ccx.tcx, f.pat)); + bcx = trans_pat_match(r.bcx, f.pat, v, next_cx).bcx; + } + ret rslt(bcx, llval); + } } } @@ -4896,6 +4913,21 @@ fn trans_pat_binding(&@block_ctxt cx, &@ast::pat pat, ValueRef llval, } ret rslt(this_cx, llval); } + case (ast::pat_rec(?field_pats, _)) { + auto bcx = cx; + auto ccx = cx.fcx.lcx.ccx; + auto rec_ty = pat_ty(ccx.tcx, pat); + auto fields = alt (ty::struct(ccx.tcx, rec_ty)) { + ty::ty_rec(?fields) { fields } + }; + for (ast::field_pat f in field_pats) { + let uint ix = ty::field_idx(ccx.sess, f.pat.span, + f.ident, fields); + auto r = GEP_tup_like(bcx, rec_ty, llval, ~[0, ix as int]); + bcx = trans_pat_binding(bcx, f.pat, r.val, true, bound).bcx; + } + ret rslt(bcx, llval); + } } } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index 1da0b114dcf..cdd106385a5 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -1278,6 +1278,11 @@ fn replace_expr_type(&@fn_ctxt fcx, &@ast::expr expr, write::ty_fixup(fcx, expr.id, tup(new_tps, new_tyt._1)); } +// FIXME remove once std::ivec::find makes it into a snapshot +fn ivec_find[T](fn(&T) -> bool f, &T[] v) -> option::t[T] { + for (T elt in v) { if (f(elt)) { ret some[T](elt); } } + ret none[T]; +} // AST fragment checking fn check_lit(@crate_ctxt ccx, &@ast::lit lit) -> ty::t { @@ -1395,16 +1400,52 @@ fn check_pat(&@fn_ctxt fcx, &ast::pat_id_map map, &@ast::pat pat, case (_) { // FIXME: Switch expected and actual in this message? I // can never tell. - fcx.ccx.tcx.sess.span_fatal(pat.span, #fmt("mismatched types: \ - expected tag, found %s", + expected %s, found tag", ty_to_str(fcx.ccx.tcx, expected))); } } write::ty_fixup(fcx, pat.id, path_tpot); } + case (ast::pat_rec(?fields, ?etc)) { + auto ex_fields; + alt (structure_of(fcx, pat.span, expected)) { + case (ty::ty_rec(?fields)) { ex_fields = fields; } + case (_) { + fcx.ccx.tcx.sess.span_fatal + (pat.span, #fmt("mismatched types: expected %s, \ + found record", + ty_to_str(fcx.ccx.tcx, expected))); + } + }; + auto f_count = ivec::len(fields); + auto ex_f_count = ivec::len(ex_fields); + if (ex_f_count < f_count || (!etc && ex_f_count > f_count)) { + fcx.ccx.tcx.sess.span_fatal + (pat.span, #fmt("mismatched types: expected a record \ + with %u fields, found one with %u \ + fields", ex_f_count, f_count)); + } + fn matches(&str name, &ty::field f) -> bool { + ret str::eq(name, f.ident); + } + for (ast::field_pat f in fields) { + alt (ivec_find(bind matches(f.ident, _), ex_fields)) { + some(?field) { + check_pat(fcx, map, f.pat, field.mt.ty); + } + none { + fcx.ccx.tcx.sess.span_fatal + (pat.span, #fmt("mismatched types: did not \ + expect a record with a field %s", + f.ident)); + } + } + } + write::ty_only_fixup(fcx, pat.id, expected); + } } } diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs index 37d57dacf3c..6c9c2f2f249 100644 --- a/src/comp/syntax/ast.rs +++ b/src/comp/syntax/ast.rs @@ -118,11 +118,14 @@ type pat = rec(node_id id, pat_ node, span span); +type field_pat = rec(ident ident, @pat pat); + tag pat_ { pat_wild; pat_bind(ident); pat_lit(@lit); pat_tag(path, (@pat)[]); + pat_rec(field_pat[], bool); } type pat_id_map = std::map::hashmap[str, ast::node_id]; @@ -137,6 +140,9 @@ fn pat_id_map(&@pat pat) -> pat_id_map { pat_tag(_, ?sub) { for (@pat p in sub) { walk(map, p); } } + pat_rec(?fields, _) { + for (field_pat f in fields) { walk(map, f.pat); } + } _ {} } } diff --git a/src/comp/syntax/fold.rs b/src/comp/syntax/fold.rs index 8529542bf44..571473261ff 100644 --- a/src/comp/syntax/fold.rs +++ b/src/comp/syntax/fold.rs @@ -267,6 +267,13 @@ fn noop_fold_pat(&pat_ p, ast_fold fld) -> pat_ { case (pat_tag(?pth, ?pats)) { pat_tag(fld.fold_path(pth), ivec::map(fld.fold_pat, pats)) } + case (pat_rec(?fields, ?etc)) { + auto fs = ~[]; + for (ast::field_pat f in fields) { + fs += ~[rec(ident=f.ident, pat=fld.fold_pat(f.pat))]; + } + pat_rec(fs, etc) + } }; } diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index 567aa995fd6..29b12763d91 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -1455,6 +1455,45 @@ fn parse_pat(&parser p) -> @ast::pat { } } } + case (token::LBRACE) { + p.bump(); + auto fields = ~[]; + auto etc = false; + auto first = true; + while (p.peek() != token::RBRACE) { + if (p.peek() == token::DOT) { + p.bump(); + expect(p, token::DOT); + expect(p, token::DOT); + if (p.peek() != token::RBRACE) { + p.fatal("expecting }, found " + + token::to_str(p.get_reader(), p.peek())); + } + etc = true; + break; + } + if (first) { first = false; } + else { expect(p, token::COMMA); } + auto fieldname = parse_ident(p); + auto subpat; + if (p.peek() == token::COLON) { + p.bump(); + subpat = parse_pat(p); + } else { + if (p.get_bad_expr_words().contains_key(fieldname)) { + p.fatal("found " + fieldname + + " in binding position"); + } + subpat = @rec(id=p.get_id(), + node=ast::pat_bind(fieldname), + span=rec(lo=lo, hi=hi)); + } + fields += ~[rec(ident=fieldname, pat=subpat)]; + } + hi = p.get_hi_pos(); + p.bump(); + pat = ast::pat_rec(fields, etc); + } case (?tok) { if (!is_ident(tok) || is_word(p, "true") || is_word(p, "false")) { auto lit = parse_lit(p); diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs index 2bc4efb1e59..e62b37807f2 100644 --- a/src/comp/syntax/print/pprust.rs +++ b/src/comp/syntax/print/pprust.rs @@ -1120,6 +1120,22 @@ fn print_pat(&ps s, &@ast::pat pat) { pclose(s); } } + case (ast::pat_rec(?fields, ?etc)) { + bopen(s); + fn print_field(&ps s, &ast::field_pat f) { + cbox(s, indent_unit); + word(s.s, f.ident); + word(s.s, ":"); + print_pat(s, f.pat); + end(s); + } + fn get_span(&ast::field_pat f) -> codemap::span { + ret f.pat.span; + } + commasep_cmnt_ivec(s, consistent, fields, print_field, get_span); + if (etc) { space(s.s); word(s.s, "..."); } + bclose(s, pat.span); + } } s.ann.post(ann_node); } diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs index c6e4d1911d4..470a8e4e38d 100644 --- a/src/comp/syntax/visit.rs +++ b/src/comp/syntax/visit.rs @@ -190,6 +190,9 @@ fn visit_pat[E](&@pat p, &E e, &vt[E] v) { for (@ty tp in path.node.types) { v.visit_ty(tp, e, v); } for (@pat child in children) { v.visit_pat(child, e, v); } } + case (pat_rec(?fields, _)) { + for (field_pat f in fields) { v.visit_pat(f.pat, e, v); } + } case (_) { } } } diff --git a/src/comp/syntax/walk.rs b/src/comp/syntax/walk.rs index 77b13f0c9b8..024437e4327 100644 --- a/src/comp/syntax/walk.rs +++ b/src/comp/syntax/walk.rs @@ -189,6 +189,9 @@ fn walk_pat(&ast_visitor v, &@ast::pat p) { for (@ast::ty tp in path.node.types) { walk_ty(v, tp); } for (@ast::pat child in children) { walk_pat(v, child); } } + case (ast::pat_rec(?fields, _)) { + for (ast::field_pat f in fields) { walk_pat(v, f.pat); } + } case (_) { } } v.visit_pat_post(p); diff --git a/src/test/compile-fail/bad-record-pat-2.rs b/src/test/compile-fail/bad-record-pat-2.rs new file mode 100644 index 00000000000..037658f631b --- /dev/null +++ b/src/test/compile-fail/bad-record-pat-2.rs @@ -0,0 +1,7 @@ +// error-pattern:did not expect a record with a field q + +fn main() { + alt rec(x=1, y=2) { + {x, q} {} + } +} diff --git a/src/test/compile-fail/bad-record-pat.rs b/src/test/compile-fail/bad-record-pat.rs new file mode 100644 index 00000000000..9813267f432 --- /dev/null +++ b/src/test/compile-fail/bad-record-pat.rs @@ -0,0 +1,7 @@ +// error-pattern:expected a record with 2 fields, found one with 1 + +fn main() { + alt rec(x=1, y=2) { + {x} {} + } +} diff --git a/src/test/run-pass/record-pat.rs b/src/test/run-pass/record-pat.rs new file mode 100644 index 00000000000..b03bc6476b1 --- /dev/null +++ b/src/test/run-pass/record-pat.rs @@ -0,0 +1,15 @@ +tag t1 { a(int); b(uint); } +type t2 = rec(t1 x, int y); +tag t3 { c(t2, uint); } + +fn m(&t3 in) -> int { + alt in { + c({x: a(?m) ...}, _) { ret m; } + c({x: b(?m), y}, ?z) { ret (m + z) as int + y; } + } +} + +fn main() { + assert m(c(rec(x=a(10), y=5), 4u)) == 10; + assert m(c(rec(x=b(10u), y=5), 4u)) == 19; +}