1
Fork 0

Allow binding of nested patterns

See src/test/run-pass/nested-patterns.rs for some examples. The syntax is

    boundvar@subpattern

Which will match the subpattern as usual, but also bind boundvar to the
whole matched value.

Closes #838
This commit is contained in:
Marijn Haverbeke 2011-12-08 11:56:16 +01:00
parent 8c966b7b18
commit 9a269a3aa8
17 changed files with 132 additions and 79 deletions

View file

@ -585,8 +585,9 @@ fn pattern_roots(tcx: ty::ctxt, mut: option::t<unsafe_ty>, pat: @ast::pat)
&set: [pattern_root]) {
alt pat.node {
ast::pat_wild. | ast::pat_lit(_) | ast::pat_range(_, _) {}
ast::pat_bind(nm) {
ast::pat_bind(nm, sub) {
set += [{id: pat.id, name: nm, mut: mut, span: pat.span}];
alt sub { some(p) { walk(tcx, mut, p, set); } _ {} }
}
ast::pat_tag(_, ps) | ast::pat_tup(ps) {
for p in ps { walk(tcx, mut, p, set); }

View file

@ -2,6 +2,7 @@ import syntax::ast::*;
import syntax::ast_util::{variant_def_ids, dummy_sp, compare_lit_exprs,
lit_expr_eq};
import syntax::visit;
import std::option::{some, none};
fn check_crate(tcx: ty::ctxt, crate: @crate) {
let v =
@ -64,57 +65,58 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
}
alt a.node {
pat_wild. | pat_bind(_) { ret true; }
pat_bind(_, some(p)) { pattern_supersedes(tcx, p, b) }
pat_wild. | pat_bind(_, none.) { true }
pat_lit(la) {
alt b.node {
pat_lit(lb) { ret lit_expr_eq(la, lb); }
_ { ret false; }
pat_lit(lb) { lit_expr_eq(la, lb) }
_ { false }
}
}
pat_tag(va, suba) {
alt b.node {
pat_tag(vb, subb) {
ret tcx.def_map.get(a.id) == tcx.def_map.get(b.id) &&
patterns_supersede(tcx, suba, subb);
tcx.def_map.get(a.id) == tcx.def_map.get(b.id) &&
patterns_supersede(tcx, suba, subb)
}
_ { ret false; }
_ { false }
}
}
pat_rec(suba, _) {
alt b.node {
pat_rec(subb, _) { ret field_patterns_supersede(tcx, suba, subb); }
_ { ret false; }
pat_rec(subb, _) { field_patterns_supersede(tcx, suba, subb) }
_ { false }
}
}
pat_tup(suba) {
alt b.node {
pat_tup(subb) { ret patterns_supersede(tcx, suba, subb); }
_ { ret false; }
pat_tup(subb) { patterns_supersede(tcx, suba, subb) }
_ { false }
}
}
pat_box(suba) {
alt b.node {
pat_box(subb) { ret pattern_supersedes(tcx, suba, subb); }
_ { ret pattern_supersedes(tcx, suba, b); }
pat_box(subb) { pattern_supersedes(tcx, suba, subb) }
_ { pattern_supersedes(tcx, suba, b) }
}
}
pat_uniq(suba) {
alt b.node {
pat_uniq(subb) { ret pattern_supersedes(tcx, suba, subb); }
_ { ret pattern_supersedes(tcx, suba, b); }
pat_uniq(subb) { pattern_supersedes(tcx, suba, subb) }
_ { pattern_supersedes(tcx, suba, b) }
}
}
pat_range(begina, enda) {
alt b.node {
pat_lit(lb) {
ret compare_lit_exprs(begina, lb) <= 0 &&
compare_lit_exprs(enda, lb) >= 0;
compare_lit_exprs(begina, lb) <= 0 &&
compare_lit_exprs(enda, lb) >= 0
}
pat_range(beginb, endb) {
ret compare_lit_exprs(begina, beginb) <= 0 &&
compare_lit_exprs(enda, endb) >= 0;
compare_lit_exprs(begina, beginb) <= 0 &&
compare_lit_exprs(enda, endb) >= 0
}
_ { ret false; }
_ { false }
}
}
}
@ -130,25 +132,26 @@ fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) {
fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
alt pat.node {
pat_wild. | pat_bind(_) { ret false; }
pat_lit(_) { ret true; }
pat_box(sub) { ret is_refutable(tcx, sub); }
pat_uniq(sub) { ret is_refutable(tcx, sub); }
pat_box(sub) | pat_uniq(sub) | pat_bind(_, some(sub)) {
is_refutable(tcx, sub)
}
pat_wild. | pat_bind(_, none.) { false }
pat_lit(_) { true }
pat_rec(fields, _) {
for field: field_pat in fields {
if is_refutable(tcx, field.pat) { ret true; }
}
ret false;
false
}
pat_tup(elts) {
for elt in elts { if is_refutable(tcx, elt) { ret true; } }
ret false;
false
}
pat_tag(_, args) {
let vdef = variant_def_ids(tcx.def_map.get(pat.id));
if std::vec::len(ty::tag_variants(tcx, vdef.tg)) != 1u { ret true; }
for p: @pat in args { if is_refutable(tcx, p) { ret true; } }
ret false;
false
}
}
}

View file

@ -819,7 +819,7 @@ fn lookup_in_ty_params(name: ident, ty_params: [ast::ty_param]) ->
fn lookup_in_pat(name: ident, pat: @ast::pat) -> option::t<def_id> {
let found = none;
ast_util::pat_bindings(pat) {|bound|
let p_name = alt bound.node { ast::pat_bind(n) { n } };
let p_name = alt bound.node { ast::pat_bind(n, _) { n } };
if str::eq(p_name, name) { found = some(local_def(bound.id)); }
};
ret found;
@ -1376,7 +1376,7 @@ fn check_item(e: @env, i: @ast::item, &&x: (), v: vt<()>) {
fn check_pat(ch: checker, p: @ast::pat) {
ast_util::pat_bindings(p) {|p|
let ident = alt p.node { pat_bind(n) { n } };
let ident = alt p.node { pat_bind(n, _) { n } };
add_name(ch, p.span, ident);
};
}
@ -1424,7 +1424,7 @@ fn check_block(e: @env, b: ast::blk, &&x: (), v: vt<()>) {
let local_values = checker(*e, "value");
for (_, loc) in locs {
ast_util::pat_bindings(loc.node.pat) {|p|
let ident = alt p.node { pat_bind(n) { n } };
let ident = alt p.node { pat_bind(n, _) { n } };
add_name(local_values, p.span, ident);
check_name(values, p.span, ident);
};

View file

@ -4702,7 +4702,7 @@ fn alloc_ty(cx: @block_ctxt, t: ty::t) -> result {
fn alloc_local(cx: @block_ctxt, local: @ast::local) -> @block_ctxt {
let t = node_id_type(bcx_ccx(cx), local.node.id);
let is_simple = alt local.node.pat.node {
ast::pat_bind(_) { true } _ { false }
ast::pat_bind(_, none.) { true } _ { false }
};
// Do not allocate space for locals that can be kept immediate.
let ccx = bcx_ccx(cx);
@ -4716,7 +4716,7 @@ fn alloc_local(cx: @block_ctxt, local: @ast::local) -> @block_ctxt {
}
let r = alloc_ty(cx, t);
alt local.node.pat.node {
ast::pat_bind(ident) {
ast::pat_bind(ident, none.) {
if bcx_ccx(cx).sess.get_opts().debuginfo {
let _: () = str::as_buf(ident, {|buf|
llvm::LLVMSetValueName(r.val, buf)

View file

@ -88,14 +88,31 @@ type match_branch =
id_map: ast_util::pat_id_map}};
type match = [match_branch];
fn matches_always(p: @ast::pat) -> bool {
ret alt p.node {
ast::pat_wild. { true }
ast::pat_bind(_) { true }
ast::pat_rec(_, _) { true }
ast::pat_tup(_) { true }
_ { false }
};
fn has_nested_bindings(m: match, col: uint) -> bool {
for br in m {
alt br.pats[col].node {
ast::pat_bind(_, some(_)) { ret true; }
_ {}
}
}
ret false;
}
fn expand_nested_bindings(m: match, col: uint, val: ValueRef) -> match {
let result = [];
for br in m {
alt br.pats[col].node {
ast::pat_bind(name, some(inner)) {
let pats = vec::slice(br.pats, 0u, col) + [inner] +
vec::slice(br.pats, col + 1u, vec::len(br.pats));
result += [@{pats: pats,
bound: br.bound + [{ident: name, val: val}]
with *br}];
}
_ { result += [br]; }
}
}
result
}
type enter_pat = fn@(@ast::pat) -> option::t<[@ast::pat]>;
@ -109,7 +126,7 @@ fn enter_match(m: match, col: uint, val: ValueRef, e: enter_pat) -> match {
vec::slice(br.pats, col + 1u, vec::len(br.pats));
let new_br = @{pats: pats,
bound: alt br.pats[col].node {
ast::pat_bind(name) {
ast::pat_bind(name, none.) {
br.bound + [{ident: name, val: val}]
}
_ { br.bound }
@ -123,6 +140,13 @@ fn enter_match(m: match, col: uint, val: ValueRef, e: enter_pat) -> match {
}
fn enter_default(m: match, col: uint, val: ValueRef) -> match {
fn matches_always(p: @ast::pat) -> bool {
ret alt p.node {
ast::pat_wild. | ast::pat_bind(_, none.) | ast::pat_rec(_, _) |
ast::pat_tup(_) { true }
_ { false }
};
}
fn e(p: @ast::pat) -> option::t<[@ast::pat]> {
ret if matches_always(p) { some([]) } else { none };
}
@ -303,18 +327,17 @@ type exit_node = {bound: bind_map, from: BasicBlockRef, to: BasicBlockRef};
type mk_fail = fn@() -> BasicBlockRef;
fn pick_col(m: match) -> uint {
fn score(p: @ast::pat) -> uint {
alt p.node {
ast::pat_lit(_) | ast::pat_tag(_, _) | ast::pat_range(_, _) { 1u }
ast::pat_bind(_, some(p)) { score(p) }
_ { 0u }
}
}
let scores = vec::init_elt_mut(0u, vec::len(m[0].pats));
for br: match_branch in m {
let i = 0u;
for p: @ast::pat in br.pats {
alt p.node {
ast::pat_lit(_) | ast::pat_tag(_, _) | ast::pat_range(_, _) {
scores[i] += 1u;
}
_ { }
}
i += 1u;
}
for p: @ast::pat in br.pats { scores[i] += score(p); i += 1u; }
}
let max_score = 0u;
let best_col = 0u;
@ -368,6 +391,9 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
let col = pick_col(m);
let val = vals[col];
let m = has_nested_bindings(m, col) ?
expand_nested_bindings(m, col, val) : m;
let vals_left =
vec::slice(vals, 0u, col) +
vec::slice(vals, col + 1u, vec::len(vals));
@ -662,7 +688,7 @@ fn bind_irrefutable_pat(bcx: @block_ctxt, pat: @ast::pat, val: ValueRef,
make_copy: bool) -> @block_ctxt {
let ccx = bcx.fcx.lcx.ccx, bcx = bcx;
alt pat.node {
ast::pat_bind(_) {
ast::pat_bind(_, inner) {
if make_copy || ccx.copy_map.contains_key(pat.id) {
let ty = ty::node_id_to_monotype(ccx.tcx, pat.id);
// FIXME: Could constrain pat_bind to make this
@ -676,6 +702,10 @@ fn bind_irrefutable_pat(bcx: @block_ctxt, pat: @ast::pat, val: ValueRef,
bcx.fcx.lllocals.insert(pat.id, local_mem(alloc));
trans_common::add_clean(bcx, alloc, ty);
} else { bcx.fcx.lllocals.insert(pat.id, local_mem(val)); }
alt inner {
some(pat) { bcx = bind_irrefutable_pat(bcx, pat, val, true); }
_ {}
}
}
ast::pat_tag(_, sub) {
if vec::len(sub) == 0u { ret bcx; }

View file

@ -1048,7 +1048,7 @@ type binding = {lhs: [inst], rhs: option::t<initializer>};
fn local_to_bindings(loc: @local) -> binding {
let lhs = [];
pat_bindings(loc.node.pat) {|p|
let ident = alt p.node { pat_bind(name) { name } };
let ident = alt p.node { pat_bind(name, _) { name } };
lhs += [{ident: ident, node: p.id}];
};
{lhs: lhs, rhs: loc.node.init}

View file

@ -12,7 +12,7 @@ type ctxt = {cs: @mutable [sp_constr], tcx: ty::ctxt};
fn collect_local(loc: @local, cx: ctxt, v: visit::vt<ctxt>) {
pat_bindings(loc.node.pat) {|p|
let ident = alt p.node { pat_bind(id) { id } };
let ident = alt p.node { pat_bind(id, _) { id } };
log "collect_local: pushing " + ident;;
*cx.cs += [respan(loc.span, ninit(p.id, ident))];
};

View file

@ -108,7 +108,7 @@ fn find_pre_post_loop(fcx: fn_ctxt, l: @local, index: @expr, body: blk,
find_pre_post_expr(fcx, index);
find_pre_post_block(fcx, body);
pat_bindings(l.node.pat) {|p|
let ident = alt p.node { pat_bind(id) { id } };
let ident = alt p.node { pat_bind(id, _) { id } };
let v_init = ninit(p.id, ident);
relax_precond_block(fcx, bit_num(fcx, v_init) as node_id, body);
// Hack: for-loop index variables are frequently ignored,
@ -579,11 +579,7 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
/* FIXME: This won't be necessary when typestate
works well enough for pat_bindings to return a
refinement-typed thing. */
let ident = alt pat.node {
pat_bind(n) { n }
_ { fcx.ccx.tcx.sess.span_bug(pat.span,
"Impossible LHS"); }
};
let ident = alt pat.node { pat_bind(n, _) { n } };
alt p {
some(p) {
copy_in_postcond(fcx, id,
@ -612,14 +608,10 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
postconds of the RHSs themselves */
pat_bindings(alocal.node.pat) {|pat|
alt pat.node {
pat_bind(n) {
pat_bind(n, _) {
set_in_postcond(bit_num(fcx, ninit(pat.id, n)),
prev_pp);
}
_ {
fcx.ccx.tcx.sess.span_bug(pat.span,
"Impossible LHS");
}
}
};
copy_pre_post_(fcx.ccx, id, prev_pp.precondition,

View file

@ -206,7 +206,7 @@ fn find_pre_post_state_loop(fcx: fn_ctxt, pres: prestate, l: @local,
// in the body
let index_post = tritv_clone(expr_poststate(fcx.ccx, index));
pat_bindings(l.node.pat) {|p|
let ident = alt p.node { pat_bind(name) { name } };
let ident = alt p.node { pat_bind(name, _) { name } };
set_in_poststate_ident(fcx, p.id, ident, index_post);
};

View file

@ -1178,7 +1178,7 @@ fn gather_locals(ccx: @crate_ctxt, f: ast::_fn, id: ast::node_id,
let visit_pat =
lambda (p: @ast::pat, &&e: (), v: visit::vt<()>) {
alt p.node {
ast::pat_bind(_) { assign(p.id, none); }
ast::pat_bind(_, _) { assign(p.id, none); }
_ {/* no-op */ }
}
visit::visit_pat(p, e, v);
@ -1248,7 +1248,7 @@ fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat,
}
write::ty_only_fixup(fcx, pat.id, b_ty);
}
ast::pat_bind(name) {
ast::pat_bind(name, sub) {
let vid = lookup_local(fcx, pat.span, pat.id);
let typ = ty::mk_var(fcx.ccx.tcx, vid);
typ = demand::simple(fcx, pat.span, expected, typ);
@ -1260,6 +1260,10 @@ fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat,
typ = demand::simple(fcx, pat.span, ct, typ);
}
write::ty_only_fixup(fcx, pat.id, typ);
alt sub {
some(p) { check_pat(fcx, map, p, expected); }
_ {}
}
}
ast::pat_tag(path, subpats) {
// Typecheck the path.

View file

@ -92,7 +92,7 @@ type field_pat = {ident: ident, pat: @pat};
tag pat_ {
pat_wild;
pat_bind(ident);
pat_bind(ident, option::t<@pat>);
pat_tag(@path, [@pat]);
pat_rec([field_pat], bool);
pat_tup([@pat]);

View file

@ -58,7 +58,7 @@ type pat_id_map = std::map::hashmap<str, node_id>;
fn pat_id_map(pat: @pat) -> pat_id_map {
let map = std::map::new_str_hash::<node_id>();
pat_bindings(pat) {|bound|
let name = alt bound.node { pat_bind(n) { n } };
let name = alt bound.node { pat_bind(n, _) { n } };
map.insert(name, bound.id);
};
ret map;
@ -67,7 +67,8 @@ fn pat_id_map(pat: @pat) -> pat_id_map {
// FIXME: could return a constrained type
fn pat_bindings(pat: @pat, it: block(@pat)) {
alt pat.node {
pat_bind(_) { it(pat); }
pat_bind(_, option::none.) { it(pat); }
pat_bind(_, option::some(sub)) { it(pat); pat_bindings(sub, it); }
pat_tag(_, sub) { for p in sub { pat_bindings(p, it); } }
pat_rec(fields, _) { for f in fields { pat_bindings(f.pat, it); } }
pat_tup(elts) { for elt in elts { pat_bindings(elt, it); } }

View file

@ -270,7 +270,9 @@ fn noop_fold_arm(a: arm, fld: ast_fold) -> arm {
fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ {
ret alt p {
pat_wild. { p }
pat_bind(ident) { pat_bind(fld.fold_ident(ident)) }
pat_bind(ident, sub) {
pat_bind(fld.fold_ident(ident), option::map(fld.fold_pat, sub))
}
pat_lit(_) { p }
pat_tag(pth, pats) {
pat_tag(fld.fold_path(pth), vec::map(fld.fold_pat, pats))

View file

@ -1429,10 +1429,9 @@ fn parse_pat(p: parser) -> @ast::pat {
if p.get_bad_expr_words().contains_key(fieldname) {
p.fatal("found " + fieldname + " in binding position");
}
subpat =
@{id: p.get_id(),
node: ast::pat_bind(fieldname),
span: ast_util::mk_sp(lo, hi)};
subpat = @{id: p.get_id(),
node: ast::pat_bind(fieldname, none),
span: ast_util::mk_sp(lo, hi)};
}
fields += [{ident: fieldname, pat: subpat}];
}
@ -1479,7 +1478,9 @@ fn parse_pat(p: parser) -> @ast::pat {
_ { true }
} {
hi = p.get_hi_pos();
pat = ast::pat_bind(parse_value_ident(p));
let name = parse_value_ident(p);
let sub = eat(p, token::AT) ? some(parse_pat(p)) : none;
pat = ast::pat_bind(name, sub);
} else {
let tag_path = parse_path_and_ty_param_substs(p);
hi = tag_path.span.hi;

View file

@ -1062,7 +1062,13 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
s.ann.pre(ann_node);
alt pat.node {
ast::pat_wild. { word(s.s, "_"); }
ast::pat_bind(id) { word(s.s, id); }
ast::pat_bind(id, sub) {
word(s.s, id);
alt sub {
some(p) { word(s.s, "@"); print_pat(s, p); }
_ {}
}
}
ast::pat_tag(path, args) {
print_path(s, path, true);
if vec::len(args) > 0u {

View file

@ -159,8 +159,9 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) {
for f: field_pat in fields { v.visit_pat(f.pat, e, v); }
}
pat_tup(elts) { for elt in elts { v.visit_pat(elt, e, v); } }
pat_box(inner) { v.visit_pat(inner, e, v); }
pat_uniq(inner) { v.visit_pat(inner, e, v); }
pat_box(inner) | pat_uniq(inner) | pat_bind(_, some(inner)) {
v.visit_pat(inner, e, v);
}
_ { }
}
}

View file

@ -0,0 +1,12 @@
fn main() {
alt {a: 10, b: @20} {
x@{a, b: @20} { assert x.a == 10; assert a == 10; }
{b, _} { fail; }
}
let x@{b, _} = {a: 10, b: {mutable c: 20}};
x.b.c = 30;
assert b.c == 20;
let y@{d, _} = {a: 10, d: {mutable c: 20}};
y.d.c = 30;
assert d.c == 20;
}