Skip to content

Commit 37b0549

Browse files
committed
Add new syntax for patterns that match the head constructor only
Adds a new kind of pattern C(*) where C is a constructor that may have any number of fields. This pattern matches any value constructed with C, without binding names for any of the fields. Closes #1701.
1 parent 087b12a commit 37b0549

File tree

12 files changed

+105
-44
lines changed

12 files changed

+105
-44
lines changed

src/librustsyntax/ast.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ enum pat_ {
148148
// records this pattern's node_id in an auxiliary
149149
// set (of "pat_idents that refer to nullary enums")
150150
pat_ident(@path, option<@pat>),
151-
pat_enum(@path, [@pat]),
151+
pat_enum(@path, option<[@pat]>), // "none" means a * pattern where
152+
// we don't bind the fields to names
152153
pat_rec([field_pat], bool),
153154
pat_tup([@pat]),
154155
pat_box(@pat),

src/librustsyntax/ext/auto_serialize.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ fn ser_enum(cx: ext_ctxt, tps: ser_tps_map, e_name: str,
779779
if vec::is_empty(pats) {
780780
ast::pat_ident(cx.path(v_span, [v_name]), none)
781781
} else {
782-
ast::pat_enum(cx.path(v_span, [v_name]), pats)
782+
ast::pat_enum(cx.path(v_span, [v_name]), some(pats))
783783
}
784784
},
785785

src/librustsyntax/fold.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,8 @@ fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ {
348348
}
349349
pat_lit(e) { pat_lit(fld.fold_expr(e)) }
350350
pat_enum(pth, pats) {
351-
pat_enum(fld.fold_path(pth), vec::map(pats, fld.fold_pat))
351+
pat_enum(fld.fold_path(pth), option::map(pats)
352+
{|pats| vec::map(pats, fld.fold_pat)})
352353
}
353354
pat_rec(fields, etc) {
354355
let mut fs = [];

src/librustsyntax/parse/parser.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -1417,24 +1417,38 @@ fn parse_pat(p: parser) -> @ast::pat {
14171417
} else {
14181418
let enum_path = parse_path_and_ty_param_substs(p, true);
14191419
hi = enum_path.span.hi;
1420-
let mut args: [@ast::pat];
1420+
let mut args: [@ast::pat] = [];
1421+
let mut star_pat = false;
14211422
alt p.token {
14221423
token::LPAREN {
1423-
let a =
1424-
parse_seq(token::LPAREN, token::RPAREN,
1425-
seq_sep(token::COMMA), parse_pat, p);
1426-
args = a.node;
1427-
hi = a.span.hi;
1424+
alt p.look_ahead(1u) {
1425+
token::BINOP(token::STAR) {
1426+
// This is a "top constructor only" pat
1427+
p.bump(); p.bump();
1428+
star_pat = true;
1429+
expect(p, token::RPAREN);
1430+
}
1431+
_ {
1432+
let a =
1433+
parse_seq(token::LPAREN, token::RPAREN,
1434+
seq_sep(token::COMMA), parse_pat, p);
1435+
args = a.node;
1436+
hi = a.span.hi;
1437+
}
1438+
}
14281439
}
1429-
_ { args = []; }
1440+
_ { }
14301441
}
14311442
// at this point, we're not sure whether it's a enum or a bind
1432-
if vec::len(args) == 0u &&
1443+
if star_pat {
1444+
pat = ast::pat_enum(enum_path, none);
1445+
}
1446+
else if vec::is_empty(args) &&
14331447
vec::len(enum_path.node.idents) == 1u {
14341448
pat = ast::pat_ident(enum_path, none);
14351449
}
14361450
else {
1437-
pat = ast::pat_enum(enum_path, args);
1451+
pat = ast::pat_enum(enum_path, some(args));
14381452
}
14391453
}
14401454
}

src/librustsyntax/print/pprust.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -1248,16 +1248,21 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
12481248
print_path(s, path, true);
12491249
alt sub {
12501250
some(p) { word(s.s, "@"); print_pat(s, p); }
1251-
_ {}
1251+
none {}
12521252
}
12531253
}
1254-
ast::pat_enum(path, args) {
1254+
ast::pat_enum(path, args_) {
12551255
print_path(s, path, true);
1256-
if vec::len(args) > 0u {
1257-
popen(s);
1258-
commasep(s, inconsistent, args, print_pat);
1259-
pclose(s);
1260-
} else { }
1256+
alt args_ {
1257+
none { word(s.s, "(*)"); }
1258+
some(args) {
1259+
if vec::len(args) > 0u {
1260+
popen(s);
1261+
commasep(s, inconsistent, args, print_pat);
1262+
pclose(s);
1263+
} else { }
1264+
}
1265+
}
12611266
}
12621267
ast::pat_rec(fields, etc) {
12631268
word(s.s, "{");

src/librustsyntax/visit.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) {
220220
alt p.node {
221221
pat_enum(path, children) {
222222
visit_path(path, e, v);
223-
for children.each {|child| v.visit_pat(child, e, v); }
223+
option::iter(children) {|children|
224+
for children.each {|child| v.visit_pat(child, e, v); }}
224225
}
225226
pat_rec(fields, _) {
226227
for fields.each {|f| v.visit_pat(f.pat, e, v); }
@@ -231,7 +232,7 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) {
231232
}
232233
pat_ident(path, inner) {
233234
visit_path(path, e, v);
234-
option::iter(inner, {|subpat| v.visit_pat(subpat, e, v)});
235+
option::iter(inner) {|subpat| v.visit_pat(subpat, e, v)};
235236
}
236237
pat_lit(ex) { v.visit_expr(ex, e, v); }
237238
pat_range(e1, e2) { v.visit_expr(e1, e, v); v.visit_expr(e2, e, v); }

src/rustc/middle/alias.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -600,11 +600,11 @@ fn pattern_roots(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat)
600600
if !pat_util::pat_is_variant(tcx.def_map, pat) {
601601
set += [{id: pat.id, name: path_to_ident(nm), mutbl: mutbl,
602602
span: pat.span}];
603-
alt sub { some(p) { walk(tcx, mutbl, p, set); } _ {} }
603+
option::iter(sub) {|p| walk(tcx, mutbl, p, set); };
604604
}
605605
ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) |
606-
ast::pat_ident(_, _) {}
607-
ast::pat_enum(_, ps) | ast::pat_tup(ps) {
606+
ast::pat_ident(_, _) | ast::pat_enum(_, none) {}
607+
ast::pat_enum(_, some(ps)) | ast::pat_tup(ps) {
608608
for ps.each {|p| walk(tcx, mutbl, p, set); }
609609
}
610610
ast::pat_rec(fs, _) {

src/rustc/middle/check_alt.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import syntax::ast::*;
33
import syntax::ast_util::{variant_def_ids, dummy_sp, unguarded_pat};
44
import middle::const_eval::{compare_lit_exprs, lit_expr_eq};
55
import syntax::codemap::span;
6+
import syntax::print::pprust::pat_to_str;
67
import pat_util::*;
78
import syntax::visit;
89
import driver::session::session;
@@ -166,13 +167,22 @@ fn check_exhaustive_enum(tcx: ty::ctxt, enum_id: def_id, sp: span,
166167
def_variant(_, id) {
167168
let variant_idx =
168169
option::get(vec::position(*variants, {|v| v.id == id}));
170+
let arg_len = variants[variant_idx].args.len();
169171
columns_by_variant[variant_idx].seen = true;
170172
alt pat.node {
171-
pat_enum(_, args) {
173+
pat_enum(_, some(args)) {
172174
vec::iteri(args) {|i, p|
173175
columns_by_variant[variant_idx].cols[i] += [p];
174176
}
175177
}
178+
pat_enum(_, none) {
179+
/* (*) pattern -- we fill in n '_' patterns, if the variant
180+
has n args */
181+
let wild_pat = @{id: tcx.sess.next_node_id(),
182+
node: pat_wild, span: pat.span};
183+
uint::range(0u, arg_len) {|i|
184+
columns_by_variant[variant_idx].cols[i] += [wild_pat]};
185+
}
176186
_ {}
177187
}
178188
}
@@ -225,9 +235,12 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
225235
}
226236
pat_enum(va, suba) {
227237
alt b.node {
228-
pat_enum(vb, subb) {
238+
pat_enum(vb, some(subb)) {
229239
tcx.def_map.get(a.id) == tcx.def_map.get(b.id) &&
230-
patterns_supersede(tcx, suba, subb)
240+
alt suba { none { true }
241+
some(subaa) {
242+
patterns_supersede(tcx, subaa, subb)
243+
}}
231244
}
232245
_ { false }
233246
}
@@ -310,10 +323,11 @@ fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
310323
for elts.each {|elt| if is_refutable(tcx, elt) { ret true; } }
311324
false
312325
}
313-
pat_enum(_, args) {
314-
for args.each {|p| if is_refutable(tcx, p) { ret true; } }
326+
pat_enum(_, some(args)) {
327+
for args.each {|p| if is_refutable(tcx, p) { ret true; } };
315328
false
316329
}
330+
pat_enum(_,_) { false }
317331
}
318332
}
319333

src/rustc/middle/pat_util.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ fn walk_pat(pat: @pat, it: fn(@pat)) {
5656
alt pat.node {
5757
pat_ident(pth, some(p)) { walk_pat(p, it); }
5858
pat_rec(fields, _) { for fields.each {|f| walk_pat(f.pat, it); } }
59-
pat_enum(_, s) | pat_tup(s) { for s.each {|p| walk_pat(p, it); } }
59+
pat_enum(_, some(s)) | pat_tup(s) { for s.each {|p| walk_pat(p, it); } }
6060
pat_box(s) | pat_uniq(s) { walk_pat(s, it); }
61-
pat_wild | pat_lit(_) | pat_range(_, _) | pat_ident(_, none) {}
61+
pat_wild | pat_lit(_) | pat_range(_, _) | pat_ident(_, _)
62+
| pat_enum(_, _) {}
6263
}
6364
}
6465

src/rustc/middle/trans/alt.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ fn enter_opt(tcx: ty::ctxt, m: match, opt: opt, col: uint,
163163
enter_match(tcx.def_map, m, col, val) {|p|
164164
alt p.node {
165165
ast::pat_enum(_, subpats) {
166-
if opt_eq(tcx, variant_opt(tcx, p.id), opt) { some(subpats) }
166+
if opt_eq(tcx, variant_opt(tcx, p.id), opt) {
167+
some(option::get_or_default(subpats, [])) }
167168
else { none }
168169
}
169170
ast::pat_ident(_, none) if pat_is_variant(tcx.def_map, p) {
@@ -700,16 +701,15 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
700701
let vdefs = ast_util::variant_def_ids(ccx.tcx.def_map.get(pat.id));
701702
let args = extract_variant_args(bcx, pat.id, vdefs, val);
702703
let mut i = 0;
703-
for vec::each(args.vals) {|argval|
704+
option::iter(sub) {|sub| for vec::each(args.vals) {|argval|
704705
bcx = bind_irrefutable_pat(bcx, sub[i], argval, make_copy);
705706
i += 1;
706-
}
707+
}}
707708
}
708709
ast::pat_rec(fields, _) {
709710
let rec_fields = ty::get_fields(node_id_type(bcx, pat.id));
710711
for vec::each(fields) {|f|
711712
let ix = option::get(ty::field_idx(f.ident, rec_fields));
712-
// how to get rid of this check?
713713
let fldptr = GEPi(bcx, val, [0, ix as int]);
714714
bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy);
715715
}

src/rustc/middle/typeck.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -2046,7 +2046,7 @@ fn universally_quantify_before_call(fcx: @fn_ctxt,
20462046
}
20472047

20482048
fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
2049-
subpats: [@ast::pat], expected: ty::t) {
2049+
subpats: option<[@ast::pat]>, expected: ty::t) {
20502050

20512051
// Typecheck the path.
20522052
let fcx = pcx.fcx;
@@ -2075,8 +2075,9 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
20752075
tcx, v_def_ids.enm, v_def_ids.var);
20762076
vinfo.args.map { |t| ty::subst(tcx, expected_substs, t) }
20772077
};
2078-
2079-
let subpats_len = subpats.len(), arg_len = arg_types.len();
2078+
let arg_len = arg_types.len(), subpats_len = alt subpats {
2079+
none { arg_len }
2080+
some(ps) { ps.len() }};
20802081
if arg_len > 0u {
20812082
// N-ary variant.
20822083
if arg_len != subpats_len {
@@ -2089,9 +2090,11 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
20892090
tcx.sess.span_fatal(pat.span, s);
20902091
}
20912092

2092-
vec::iter2(subpats, arg_types) {|subpat, arg_ty|
2093-
check_pat(pcx, subpat, arg_ty);
2094-
}
2093+
option::iter(subpats) {|pats|
2094+
vec::iter2(pats, arg_types) {|subpat, arg_ty|
2095+
check_pat(pcx, subpat, arg_ty);
2096+
}
2097+
};
20952098
} else if subpats_len > 0u {
20962099
tcx.sess.span_fatal
20972100
(pat.span, #fmt["this pattern has %u field%s, \
@@ -2159,8 +2162,8 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
21592162
_ {}
21602163
}
21612164
}
2162-
ast::pat_ident(path, _) {
2163-
check_pat_variant(pcx, pat, path, [], expected);
2165+
ast::pat_ident(path, c) {
2166+
check_pat_variant(pcx, pat, path, some([]), expected);
21642167
}
21652168
ast::pat_enum(path, subpats) {
21662169
check_pat_variant(pcx, pat, path, subpats, expected);
@@ -3885,7 +3888,7 @@ fn check_enum_variants(ccx: @crate_ctxt,
38853888
}) {
38863889
ccx.tcx.sess.span_err(sp, "illegal recursive enum type. \
38873890
wrap the inner value in a box to \
3888-
make it represenable");
3891+
make it representable");
38893892
}
38903893

38913894
// Check that it is possible to instantiate this enum:

src/test/run-pass/issue-1701.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
enum pattern { tabby, tortoiseshell, calico }
2+
enum breed { beagle, rottweiler, pug }
3+
type name = str;
4+
enum ear_kind { lop, upright }
5+
enum animal { cat(pattern), dog(breed), rabbit(name, ear_kind), tiger }
6+
7+
fn noise(a: animal) -> option<str> {
8+
alt a {
9+
cat(*) { some("meow") }
10+
dog(*) { some("woof") }
11+
rabbit(*) { none }
12+
tiger(*) { some("roar") }
13+
}
14+
}
15+
16+
fn main() {
17+
assert noise(cat(tabby)) == some("meow");
18+
assert noise(dog(pug)) == some("woof");
19+
assert noise(rabbit("Hilbert", upright)) == none;
20+
assert noise(tiger) == some("roar");
21+
}

0 commit comments

Comments
 (0)