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.
This commit is contained in:
Tim Chevalier 2012-04-20 00:54:42 -07:00
parent 087b12ac29
commit 37b0549730
12 changed files with 105 additions and 44 deletions

View file

@ -148,7 +148,8 @@ enum pat_ {
// records this pattern's node_id in an auxiliary
// set (of "pat_idents that refer to nullary enums")
pat_ident(@path, option<@pat>),
pat_enum(@path, [@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_tup([@pat]),
pat_box(@pat),

View file

@ -779,7 +779,7 @@ fn ser_enum(cx: ext_ctxt, tps: ser_tps_map, e_name: str,
if vec::is_empty(pats) {
ast::pat_ident(cx.path(v_span, [v_name]), none)
} else {
ast::pat_enum(cx.path(v_span, [v_name]), pats)
ast::pat_enum(cx.path(v_span, [v_name]), some(pats))
}
},

View file

@ -348,7 +348,8 @@ fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ {
}
pat_lit(e) { pat_lit(fld.fold_expr(e)) }
pat_enum(pth, pats) {
pat_enum(fld.fold_path(pth), vec::map(pats, fld.fold_pat))
pat_enum(fld.fold_path(pth), option::map(pats)
{|pats| vec::map(pats, fld.fold_pat)})
}
pat_rec(fields, etc) {
let mut fs = [];

View file

@ -1417,24 +1417,38 @@ fn parse_pat(p: parser) -> @ast::pat {
} else {
let enum_path = parse_path_and_ty_param_substs(p, true);
hi = enum_path.span.hi;
let mut args: [@ast::pat];
let mut args: [@ast::pat] = [];
let mut star_pat = false;
alt p.token {
token::LPAREN {
let a =
parse_seq(token::LPAREN, token::RPAREN,
seq_sep(token::COMMA), parse_pat, p);
args = a.node;
hi = a.span.hi;
alt p.look_ahead(1u) {
token::BINOP(token::STAR) {
// This is a "top constructor only" pat
p.bump(); p.bump();
star_pat = true;
expect(p, token::RPAREN);
}
_ {
let a =
parse_seq(token::LPAREN, token::RPAREN,
seq_sep(token::COMMA), parse_pat, p);
args = a.node;
hi = a.span.hi;
}
}
}
_ { args = []; }
_ { }
}
// at this point, we're not sure whether it's a enum or a bind
if vec::len(args) == 0u &&
if star_pat {
pat = ast::pat_enum(enum_path, none);
}
else if vec::is_empty(args) &&
vec::len(enum_path.node.idents) == 1u {
pat = ast::pat_ident(enum_path, none);
}
else {
pat = ast::pat_enum(enum_path, args);
pat = ast::pat_enum(enum_path, some(args));
}
}
}

View file

@ -1248,16 +1248,21 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
print_path(s, path, true);
alt sub {
some(p) { word(s.s, "@"); print_pat(s, p); }
_ {}
none {}
}
}
ast::pat_enum(path, args) {
ast::pat_enum(path, args_) {
print_path(s, path, true);
if vec::len(args) > 0u {
popen(s);
commasep(s, inconsistent, args, print_pat);
pclose(s);
} else { }
alt args_ {
none { word(s.s, "(*)"); }
some(args) {
if vec::len(args) > 0u {
popen(s);
commasep(s, inconsistent, args, print_pat);
pclose(s);
} else { }
}
}
}
ast::pat_rec(fields, etc) {
word(s.s, "{");

View file

@ -220,7 +220,8 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) {
alt p.node {
pat_enum(path, children) {
visit_path(path, e, v);
for children.each {|child| v.visit_pat(child, e, v); }
option::iter(children) {|children|
for children.each {|child| v.visit_pat(child, e, v); }}
}
pat_rec(fields, _) {
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>) {
}
pat_ident(path, inner) {
visit_path(path, e, v);
option::iter(inner, {|subpat| v.visit_pat(subpat, e, v)});
option::iter(inner) {|subpat| v.visit_pat(subpat, e, v)};
}
pat_lit(ex) { v.visit_expr(ex, e, v); }
pat_range(e1, e2) { v.visit_expr(e1, e, v); v.visit_expr(e2, e, v); }

View file

@ -600,11 +600,11 @@ fn pattern_roots(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat)
if !pat_util::pat_is_variant(tcx.def_map, pat) {
set += [{id: pat.id, name: path_to_ident(nm), mutbl: mutbl,
span: pat.span}];
alt sub { some(p) { walk(tcx, mutbl, p, set); } _ {} }
option::iter(sub) {|p| walk(tcx, mutbl, p, set); };
}
ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) |
ast::pat_ident(_, _) {}
ast::pat_enum(_, ps) | ast::pat_tup(ps) {
ast::pat_ident(_, _) | ast::pat_enum(_, none) {}
ast::pat_enum(_, some(ps)) | ast::pat_tup(ps) {
for ps.each {|p| walk(tcx, mutbl, p, set); }
}
ast::pat_rec(fs, _) {

View file

@ -3,6 +3,7 @@ import syntax::ast::*;
import syntax::ast_util::{variant_def_ids, dummy_sp, unguarded_pat};
import middle::const_eval::{compare_lit_exprs, lit_expr_eq};
import syntax::codemap::span;
import syntax::print::pprust::pat_to_str;
import pat_util::*;
import syntax::visit;
import driver::session::session;
@ -166,13 +167,22 @@ fn check_exhaustive_enum(tcx: ty::ctxt, enum_id: def_id, sp: span,
def_variant(_, id) {
let variant_idx =
option::get(vec::position(*variants, {|v| v.id == id}));
let arg_len = variants[variant_idx].args.len();
columns_by_variant[variant_idx].seen = true;
alt pat.node {
pat_enum(_, args) {
pat_enum(_, some(args)) {
vec::iteri(args) {|i, p|
columns_by_variant[variant_idx].cols[i] += [p];
}
}
pat_enum(_, none) {
/* (*) pattern -- we fill in n '_' patterns, if the variant
has n args */
let wild_pat = @{id: tcx.sess.next_node_id(),
node: pat_wild, span: pat.span};
uint::range(0u, arg_len) {|i|
columns_by_variant[variant_idx].cols[i] += [wild_pat]};
}
_ {}
}
}
@ -225,9 +235,12 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
}
pat_enum(va, suba) {
alt b.node {
pat_enum(vb, subb) {
pat_enum(vb, some(subb)) {
tcx.def_map.get(a.id) == tcx.def_map.get(b.id) &&
patterns_supersede(tcx, suba, subb)
alt suba { none { true }
some(subaa) {
patterns_supersede(tcx, subaa, subb)
}}
}
_ { false }
}
@ -310,10 +323,11 @@ fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
for elts.each {|elt| if is_refutable(tcx, elt) { ret true; } }
false
}
pat_enum(_, args) {
for args.each {|p| if is_refutable(tcx, p) { ret true; } }
pat_enum(_, some(args)) {
for args.each {|p| if is_refutable(tcx, p) { ret true; } };
false
}
pat_enum(_,_) { false }
}
}

View file

@ -56,9 +56,10 @@ fn walk_pat(pat: @pat, it: fn(@pat)) {
alt pat.node {
pat_ident(pth, some(p)) { walk_pat(p, it); }
pat_rec(fields, _) { for fields.each {|f| walk_pat(f.pat, it); } }
pat_enum(_, s) | pat_tup(s) { for s.each {|p| walk_pat(p, it); } }
pat_enum(_, some(s)) | pat_tup(s) { for s.each {|p| walk_pat(p, it); } }
pat_box(s) | pat_uniq(s) { walk_pat(s, it); }
pat_wild | pat_lit(_) | pat_range(_, _) | pat_ident(_, none) {}
pat_wild | pat_lit(_) | pat_range(_, _) | pat_ident(_, _)
| pat_enum(_, _) {}
}
}

View file

@ -163,7 +163,8 @@ fn enter_opt(tcx: ty::ctxt, m: match, opt: opt, col: uint,
enter_match(tcx.def_map, m, col, val) {|p|
alt p.node {
ast::pat_enum(_, subpats) {
if opt_eq(tcx, variant_opt(tcx, p.id), opt) { some(subpats) }
if opt_eq(tcx, variant_opt(tcx, p.id), opt) {
some(option::get_or_default(subpats, [])) }
else { none }
}
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,
let vdefs = ast_util::variant_def_ids(ccx.tcx.def_map.get(pat.id));
let args = extract_variant_args(bcx, pat.id, vdefs, val);
let mut i = 0;
for vec::each(args.vals) {|argval|
option::iter(sub) {|sub| for vec::each(args.vals) {|argval|
bcx = bind_irrefutable_pat(bcx, sub[i], argval, make_copy);
i += 1;
}
}}
}
ast::pat_rec(fields, _) {
let rec_fields = ty::get_fields(node_id_type(bcx, pat.id));
for vec::each(fields) {|f|
let ix = option::get(ty::field_idx(f.ident, rec_fields));
// how to get rid of this check?
let fldptr = GEPi(bcx, val, [0, ix as int]);
bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy);
}

View file

@ -2046,7 +2046,7 @@ fn universally_quantify_before_call(fcx: @fn_ctxt,
}
fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
subpats: [@ast::pat], expected: ty::t) {
subpats: option<[@ast::pat]>, expected: ty::t) {
// Typecheck the path.
let fcx = pcx.fcx;
@ -2075,8 +2075,9 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
tcx, v_def_ids.enm, v_def_ids.var);
vinfo.args.map { |t| ty::subst(tcx, expected_substs, t) }
};
let subpats_len = subpats.len(), arg_len = arg_types.len();
let arg_len = arg_types.len(), subpats_len = alt subpats {
none { arg_len }
some(ps) { ps.len() }};
if arg_len > 0u {
// N-ary variant.
if arg_len != subpats_len {
@ -2089,9 +2090,11 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
tcx.sess.span_fatal(pat.span, s);
}
vec::iter2(subpats, arg_types) {|subpat, arg_ty|
check_pat(pcx, subpat, arg_ty);
}
option::iter(subpats) {|pats|
vec::iter2(pats, arg_types) {|subpat, arg_ty|
check_pat(pcx, subpat, arg_ty);
}
};
} else if subpats_len > 0u {
tcx.sess.span_fatal
(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) {
_ {}
}
}
ast::pat_ident(path, _) {
check_pat_variant(pcx, pat, path, [], expected);
ast::pat_ident(path, c) {
check_pat_variant(pcx, pat, path, some([]), expected);
}
ast::pat_enum(path, subpats) {
check_pat_variant(pcx, pat, path, subpats, expected);
@ -3885,7 +3888,7 @@ fn check_enum_variants(ccx: @crate_ctxt,
}) {
ccx.tcx.sess.span_err(sp, "illegal recursive enum type. \
wrap the inner value in a box to \
make it represenable");
make it representable");
}
// Check that it is possible to instantiate this enum:

View file

@ -0,0 +1,21 @@
enum pattern { tabby, tortoiseshell, calico }
enum breed { beagle, rottweiler, pug }
type name = str;
enum ear_kind { lop, upright }
enum animal { cat(pattern), dog(breed), rabbit(name, ear_kind), tiger }
fn noise(a: animal) -> option<str> {
alt a {
cat(*) { some("meow") }
dog(*) { some("woof") }
rabbit(*) { none }
tiger(*) { some("roar") }
}
}
fn main() {
assert noise(cat(tabby)) == some("meow");
assert noise(dog(pug)) == some("woof");
assert noise(rabbit("Hilbert", upright)) == none;
assert noise(tiger) == some("roar");
}