Skip to content

Commit 253dfc3

Browse files
committed
rustc: Implement pattern matching for structs
1 parent 5cb3a94 commit 253dfc3

File tree

13 files changed

+445
-100
lines changed

13 files changed

+445
-100
lines changed

src/libsyntax/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ enum pat_ {
166166
pat_enum(@path, option<~[@pat]>), // "none" means a * pattern where
167167
// we don't bind the fields to names
168168
pat_rec(~[field_pat], bool),
169+
pat_struct(@path, ~[field_pat], bool),
169170
pat_tup(~[@pat]),
170171
pat_box(@pat),
171172
pat_uniq(@pat),

src/libsyntax/ast_util.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,8 @@ fn walk_pat(pat: @pat, it: fn(@pat)) {
602602
it(pat);
603603
match pat.node {
604604
pat_ident(_, pth, some(p)) => walk_pat(p, it),
605-
pat_rec(fields, _) => for fields.each |f| { walk_pat(f.pat, it) }
605+
pat_rec(fields, _) | pat_struct(_, fields, _) =>
606+
for fields.each |f| { walk_pat(f.pat, it) }
606607
pat_enum(_, some(s)) | pat_tup(s) => for s.each |p| {
607608
walk_pat(p, it)
608609
}

src/libsyntax/fold.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,16 @@ fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ {
354354
}
355355
pat_rec(fs, etc)
356356
}
357+
pat_struct(pth, fields, etc) => {
358+
let pth_ = fld.fold_path(pth);
359+
let mut fs = ~[];
360+
for fields.each |f| {
361+
vec::push(fs,
362+
{ident: /* FIXME (#2543) */ copy f.ident,
363+
pat: fld.fold_pat(f.pat)});
364+
}
365+
pat_struct(pth_, fs, etc)
366+
}
357367
pat_tup(elts) => pat_tup(vec::map(elts, |x| fld.fold_pat(x))),
358368
pat_box(inner) => pat_box(fld.fold_pat(inner)),
359369
pat_uniq(inner) => pat_uniq(fld.fold_pat(inner)),

src/libsyntax/parse/parser.rs

Lines changed: 128 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
4242
mac_ellipsis, mac_invoc, mac_invoc_tt, mac_var, matcher,
4343
match_nonterminal, match_seq, match_tok, method, mode, mt, mul,
4444
mutability, neg, noreturn, not, pat, pat_box, pat_enum,
45-
pat_ident, pat_lit, pat_range, pat_rec, pat_tup, pat_uniq,
46-
pat_wild, path, private, proto, proto_bare, proto_block,
47-
proto_box, proto_uniq, provided, public, pure_fn, purity,
48-
re_anon, re_named, region, rem, required, ret_style, return_val,
49-
self_ty, shl, shr, stmt, stmt_decl, stmt_expr, stmt_semi,
50-
subtract, sty_box, sty_by_ref, sty_region, sty_uniq, sty_value,
51-
token_tree, trait_method, trait_ref, tt_delim, tt_seq, tt_tok,
52-
tt_nonterminal, ty, ty_, ty_bot, ty_box, ty_field, ty_fn,
45+
pat_ident, pat_lit, pat_range, pat_rec, pat_struct, pat_tup,
46+
pat_uniq, pat_wild, path, private, proto, proto_bare,
47+
proto_block, proto_box, proto_uniq, provided, public, pure_fn,
48+
purity, re_anon, re_named, region, rem, required, ret_style,
49+
return_val, self_ty, shl, shr, stmt, stmt_decl, stmt_expr,
50+
stmt_semi, subtract, sty_box, sty_by_ref, sty_region, sty_uniq,
51+
sty_value, token_tree, trait_method, trait_ref, tt_delim, tt_seq,
52+
tt_tok, tt_nonterminal, ty, ty_, ty_bot, ty_box, ty_field, ty_fn,
5353
ty_infer, ty_mac, ty_method, ty_nil, ty_param, ty_path, ty_ptr,
5454
ty_rec, ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec,
5555
ty_fixed_length, unchecked_blk, uniq, unsafe_blk, unsafe_fn,
@@ -1640,6 +1640,52 @@ class parser {
16401640
};
16411641
}
16421642
1643+
fn parse_pat_fields(refutable: bool) -> (~[ast::field_pat], bool) {
1644+
let mut fields = ~[];
1645+
let mut etc = false;
1646+
let mut first = true;
1647+
while self.token != token::RBRACE {
1648+
if first { first = false; }
1649+
else { self.expect(token::COMMA); }
1650+
1651+
if self.token == token::UNDERSCORE {
1652+
self.bump();
1653+
if self.token != token::RBRACE {
1654+
self.fatal(~"expected `}`, found `" +
1655+
token_to_str(self.reader, self.token) +
1656+
~"`");
1657+
}
1658+
etc = true;
1659+
break;
1660+
}
1661+
1662+
let lo1 = self.last_span.lo;
1663+
let fieldname = if self.look_ahead(1u) == token::COLON {
1664+
self.parse_ident()
1665+
} else {
1666+
self.parse_value_ident()
1667+
};
1668+
let hi1 = self.last_span.lo;
1669+
let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1),
1670+
fieldname);
1671+
let mut subpat;
1672+
if self.token == token::COLON {
1673+
self.bump();
1674+
subpat = self.parse_pat(refutable);
1675+
} else {
1676+
subpat = @{
1677+
id: self.get_id(),
1678+
node: pat_ident(bind_by_implicit_ref,
1679+
fieldpath,
1680+
none),
1681+
span: self.last_span
1682+
};
1683+
}
1684+
vec::push(fields, {ident: fieldname, pat: subpat});
1685+
}
1686+
return (fields, etc);
1687+
}
1688+
16431689
fn parse_pat(refutable: bool) -> @pat {
16441690
maybe_whole!{self, nt_pat};
16451691

@@ -1685,48 +1731,7 @@ class parser {
16851731
}
16861732
token::LBRACE => {
16871733
self.bump();
1688-
let mut fields = ~[];
1689-
let mut etc = false;
1690-
let mut first = true;
1691-
while self.token != token::RBRACE {
1692-
if first { first = false; }
1693-
else { self.expect(token::COMMA); }
1694-
1695-
if self.token == token::UNDERSCORE {
1696-
self.bump();
1697-
if self.token != token::RBRACE {
1698-
self.fatal(~"expected `}`, found `" +
1699-
token_to_str(self.reader, self.token) +
1700-
~"`");
1701-
}
1702-
etc = true;
1703-
break;
1704-
}
1705-
1706-
let lo1 = self.last_span.lo;
1707-
let fieldname = if self.look_ahead(1u) == token::COLON {
1708-
self.parse_ident()
1709-
} else {
1710-
self.parse_value_ident()
1711-
};
1712-
let hi1 = self.last_span.lo;
1713-
let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1),
1714-
fieldname);
1715-
let mut subpat;
1716-
if self.token == token::COLON {
1717-
self.bump();
1718-
subpat = self.parse_pat(refutable);
1719-
} else {
1720-
subpat = @{
1721-
id: self.get_id(),
1722-
node: pat_ident(bind_by_implicit_ref,
1723-
fieldpath,
1724-
none),
1725-
span: mk_sp(lo, hi)
1726-
};
1727-
}
1728-
vec::push(fields, {ident: fieldname, pat: subpat});
1729-
}
1734+
let (fields, etc) = self.parse_pat_fields(refutable);
17301735
hi = self.span.hi;
17311736
self.bump();
17321737
pat = pat_rec(fields, etc);
@@ -1771,21 +1776,82 @@ class parser {
17711776
} else if !is_plain_ident(self.token) {
17721777
pat = self.parse_enum_variant(refutable);
17731778
} else {
1774-
// this is a plain identifier, like `x` or `x(...)`
1779+
let binding_mode;
1780+
if self.eat_keyword(~"copy") {
1781+
binding_mode = bind_by_value;
1782+
} else if refutable {
1783+
// XXX: Should be bind_by_value, but that's not
1784+
// backward compatible.
1785+
binding_mode = bind_by_implicit_ref;
1786+
} else {
1787+
binding_mode = bind_by_value;
1788+
}
1789+
1790+
let cannot_be_enum_or_struct;
17751791
match self.look_ahead(1) {
1776-
token::LPAREN | token::LBRACKET | token::LT => {
1777-
pat = self.parse_enum_variant(refutable);
1778-
}
1779-
_ => {
1780-
let binding_mode = if refutable {
1781-
// XXX: Should be bind_by_value, but that's not
1782-
// backward compatible.
1783-
bind_by_implicit_ref
1792+
token::LPAREN | token::LBRACKET | token::LT |
1793+
token::LBRACE =>
1794+
cannot_be_enum_or_struct = false,
1795+
_ =>
1796+
cannot_be_enum_or_struct = true
1797+
}
1798+
1799+
if is_plain_ident(self.token) && cannot_be_enum_or_struct {
1800+
let name = self.parse_value_path();
1801+
let sub;
1802+
if self.eat(token::AT) {
1803+
sub = some(self.parse_pat(refutable));
17841804
} else {
1785-
bind_by_value
1805+
sub = none;
17861806
};
1787-
pat = self.parse_pat_ident(refutable, binding_mode);
1788-
}
1807+
pat = pat_ident(binding_mode, name, sub);
1808+
} else {
1809+
let enum_path = self.parse_path_with_tps(true);
1810+
match self.token {
1811+
token::LBRACE => {
1812+
self.bump();
1813+
let (fields, etc) =
1814+
self.parse_pat_fields(refutable);
1815+
self.bump();
1816+
pat = pat_struct(enum_path, fields, etc);
1817+
}
1818+
_ => {
1819+
let mut args: ~[@pat] = ~[];
1820+
let mut star_pat = false;
1821+
match self.token {
1822+
token::LPAREN => match self.look_ahead(1u) {
1823+
token::BINOP(token::STAR) => {
1824+
// This is a "top constructor only" pat
1825+
self.bump(); self.bump();
1826+
star_pat = true;
1827+
self.expect(token::RPAREN);
1828+
}
1829+
_ => {
1830+
args = self.parse_unspanned_seq(
1831+
token::LPAREN, token::RPAREN,
1832+
seq_sep_trailing_disallowed
1833+
(token::COMMA),
1834+
|p| p.parse_pat(refutable));
1835+
}
1836+
}
1837+
_ => ()
1838+
}
1839+
// at this point, we're not sure whether it's a
1840+
// enum or a bind
1841+
if star_pat {
1842+
pat = pat_enum(enum_path, none);
1843+
}
1844+
else if vec::is_empty(args) &&
1845+
vec::len(enum_path.idents) == 1u {
1846+
pat = pat_ident(binding_mode,
1847+
enum_path,
1848+
none);
1849+
}
1850+
else {
1851+
pat = pat_enum(enum_path, some(args));
1852+
}
1853+
}
1854+
}
17891855
}
17901856
}
17911857
hi = self.span.hi;

src/libsyntax/print/pprust.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,24 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
13751375
}
13761376
word(s.s, ~"}");
13771377
}
1378+
ast::pat_struct(path, fields, etc) => {
1379+
print_path(s, path, true);
1380+
word(s.s, ~"{");
1381+
fn print_field(s: ps, f: ast::field_pat) {
1382+
cbox(s, indent_unit);
1383+
word(s.s, *f.ident);
1384+
word_space(s, ~":");
1385+
print_pat(s, f.pat);
1386+
end(s);
1387+
}
1388+
fn get_span(f: ast::field_pat) -> codemap::span { return f.pat.span; }
1389+
commasep_cmnt(s, consistent, fields, print_field, get_span);
1390+
if etc {
1391+
if vec::len(fields) != 0u { word_space(s, ~","); }
1392+
word(s.s, ~"_");
1393+
}
1394+
word(s.s, ~"}");
1395+
}
13781396
ast::pat_tup(elts) => {
13791397
popen(s);
13801398
commasep(s, inconsistent, elts, print_pat);

src/libsyntax/visit.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) {
222222
pat_rec(fields, _) => for fields.each |f| {
223223
v.visit_pat(f.pat, e, v)
224224
}
225+
pat_struct(path, fields, _) => {
226+
visit_path(path, e, v);
227+
for fields.each |f| {
228+
v.visit_pat(f.pat, e, v);
229+
}
230+
}
225231
pat_tup(elts) => for elts.each |elt| {
226232
v.visit_pat(elt, e, v)
227233
}

src/rustc/middle/borrowck/gather_loans.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,14 @@ impl methods for gather_loan_ctxt {
513513
}
514514
}
515515

516+
ast::pat_struct(_, field_pats, _) => {
517+
// {f1: p1, ..., fN: pN}
518+
for field_pats.each |fp| {
519+
let cmt_field = self.bccx.cat_field(fp.pat, cmt, fp.ident);
520+
self.gather_pat(cmt_field, fp.pat, arm_id, alt_id);
521+
}
522+
}
523+
516524
ast::pat_tup(subpats) => {
517525
// (p1, ..., pN)
518526
for subpats.each |subpat| {

src/rustc/middle/check_alt.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ fn pat_ctor_id(tcx: ty::ctxt, p: @pat) -> option<ctor> {
212212
pat_range(lo, hi) => {
213213
some(range(eval_const_expr(tcx, lo), eval_const_expr(tcx, hi)))
214214
}
215-
pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) => {
215+
pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) |
216+
pat_struct(*) => {
216217
some(single)
217218
}
218219
}
@@ -234,7 +235,8 @@ fn is_wild(tcx: ty::ctxt, p: @pat) -> bool {
234235

235236
fn missing_ctor(tcx: ty::ctxt, m: matrix, left_ty: ty::t) -> option<ctor> {
236237
match ty::get(left_ty).struct {
237-
ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_tup(_) | ty::ty_rec(_) => {
238+
ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_tup(_) | ty::ty_rec(_) |
239+
ty::ty_class(*) => {
238240
for m.each |r| {
239241
if !is_wild(tcx, r[0]) { return none; }
240242
}
@@ -286,6 +288,7 @@ fn ctor_arity(tcx: ty::ctxt, ctor: ctor, ty: ty::t) -> uint {
286288
some(v) => v.args.len()
287289
}
288290
}
291+
ty::ty_class(cid, _) => ty::lookup_class_fields(tcx, cid).len(),
289292
_ => 0u
290293
}
291294
}
@@ -327,7 +330,29 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint,
327330
};
328331
let args = vec::map(ty_flds, |ty_f| {
329332
match vec::find(flds, |f| f.ident == ty_f.ident ) {
330-
some(f) => f.pat, _ => wild()
333+
some(f) => f.pat,
334+
_ => wild()
335+
}
336+
});
337+
some(vec::append(args, vec::tail(r)))
338+
}
339+
pat_struct(_, flds, _) => {
340+
// Grab the class data that we care about.
341+
let class_fields, class_id;
342+
match ty::get(left_ty).struct {
343+
ty::ty_class(cid, substs) => {
344+
class_id = cid;
345+
class_fields = ty::lookup_class_fields(tcx, class_id);
346+
}
347+
_ => {
348+
tcx.sess.span_bug(r0.span, ~"struct pattern didn't resolve \
349+
to a struct");
350+
}
351+
}
352+
let args = vec::map(class_fields, |class_field| {
353+
match vec::find(flds, |f| f.ident == class_field.ident ) {
354+
some(f) => f.pat,
355+
_ => wild()
331356
}
332357
});
333358
some(vec::append(args, vec::tail(r)))
@@ -377,7 +402,9 @@ fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) {
377402
fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
378403
match tcx.def_map.find(pat.id) {
379404
some(def_variant(enum_id, var_id)) => {
380-
if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u { return true; }
405+
if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u {
406+
return true;
407+
}
381408
}
382409
_ => ()
383410
}
@@ -394,6 +421,12 @@ fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
394421
}
395422
false
396423
}
424+
pat_struct(_, fields, _) => {
425+
for fields.each |it| {
426+
if is_refutable(tcx, it.pat) { return true; }
427+
}
428+
false
429+
}
397430
pat_tup(elts) => {
398431
for elts.each |elt| { if is_refutable(tcx, elt) { return true; } }
399432
false

0 commit comments

Comments
 (0)