rustc: Implement pattern matching for structs
This commit is contained in:
parent
5cb3a94bfb
commit
253dfc3387
13 changed files with 445 additions and 100 deletions
|
@ -166,6 +166,7 @@ enum pat_ {
|
||||||
pat_enum(@path, option<~[@pat]>), // "none" means a * pattern where
|
pat_enum(@path, option<~[@pat]>), // "none" means a * pattern where
|
||||||
// we don't bind the fields to names
|
// we don't bind the fields to names
|
||||||
pat_rec(~[field_pat], bool),
|
pat_rec(~[field_pat], bool),
|
||||||
|
pat_struct(@path, ~[field_pat], bool),
|
||||||
pat_tup(~[@pat]),
|
pat_tup(~[@pat]),
|
||||||
pat_box(@pat),
|
pat_box(@pat),
|
||||||
pat_uniq(@pat),
|
pat_uniq(@pat),
|
||||||
|
|
|
@ -602,7 +602,8 @@ fn walk_pat(pat: @pat, it: fn(@pat)) {
|
||||||
it(pat);
|
it(pat);
|
||||||
match pat.node {
|
match pat.node {
|
||||||
pat_ident(_, pth, some(p)) => walk_pat(p, it),
|
pat_ident(_, pth, some(p)) => walk_pat(p, it),
|
||||||
pat_rec(fields, _) => for fields.each |f| { walk_pat(f.pat, it) }
|
pat_rec(fields, _) | pat_struct(_, fields, _) =>
|
||||||
|
for fields.each |f| { walk_pat(f.pat, it) }
|
||||||
pat_enum(_, some(s)) | pat_tup(s) => for s.each |p| {
|
pat_enum(_, some(s)) | pat_tup(s) => for s.each |p| {
|
||||||
walk_pat(p, it)
|
walk_pat(p, it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -354,6 +354,16 @@ fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ {
|
||||||
}
|
}
|
||||||
pat_rec(fs, etc)
|
pat_rec(fs, etc)
|
||||||
}
|
}
|
||||||
|
pat_struct(pth, fields, etc) => {
|
||||||
|
let pth_ = fld.fold_path(pth);
|
||||||
|
let mut fs = ~[];
|
||||||
|
for fields.each |f| {
|
||||||
|
vec::push(fs,
|
||||||
|
{ident: /* FIXME (#2543) */ copy f.ident,
|
||||||
|
pat: fld.fold_pat(f.pat)});
|
||||||
|
}
|
||||||
|
pat_struct(pth_, fs, etc)
|
||||||
|
}
|
||||||
pat_tup(elts) => pat_tup(vec::map(elts, |x| fld.fold_pat(x))),
|
pat_tup(elts) => pat_tup(vec::map(elts, |x| fld.fold_pat(x))),
|
||||||
pat_box(inner) => pat_box(fld.fold_pat(inner)),
|
pat_box(inner) => pat_box(fld.fold_pat(inner)),
|
||||||
pat_uniq(inner) => pat_uniq(fld.fold_pat(inner)),
|
pat_uniq(inner) => pat_uniq(fld.fold_pat(inner)),
|
||||||
|
|
|
@ -42,14 +42,14 @@ import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
|
||||||
mac_ellipsis, mac_invoc, mac_invoc_tt, mac_var, matcher,
|
mac_ellipsis, mac_invoc, mac_invoc_tt, mac_var, matcher,
|
||||||
match_nonterminal, match_seq, match_tok, method, mode, mt, mul,
|
match_nonterminal, match_seq, match_tok, method, mode, mt, mul,
|
||||||
mutability, neg, noreturn, not, pat, pat_box, pat_enum,
|
mutability, neg, noreturn, not, pat, pat_box, pat_enum,
|
||||||
pat_ident, pat_lit, pat_range, pat_rec, pat_tup, pat_uniq,
|
pat_ident, pat_lit, pat_range, pat_rec, pat_struct, pat_tup,
|
||||||
pat_wild, path, private, proto, proto_bare, proto_block,
|
pat_uniq, pat_wild, path, private, proto, proto_bare,
|
||||||
proto_box, proto_uniq, provided, public, pure_fn, purity,
|
proto_block, proto_box, proto_uniq, provided, public, pure_fn,
|
||||||
re_anon, re_named, region, rem, required, ret_style, return_val,
|
purity, re_anon, re_named, region, rem, required, ret_style,
|
||||||
self_ty, shl, shr, stmt, stmt_decl, stmt_expr, stmt_semi,
|
return_val, self_ty, shl, shr, stmt, stmt_decl, stmt_expr,
|
||||||
subtract, sty_box, sty_by_ref, sty_region, sty_uniq, sty_value,
|
stmt_semi, subtract, sty_box, sty_by_ref, sty_region, sty_uniq,
|
||||||
token_tree, trait_method, trait_ref, tt_delim, tt_seq, tt_tok,
|
sty_value, token_tree, trait_method, trait_ref, tt_delim, tt_seq,
|
||||||
tt_nonterminal, ty, ty_, ty_bot, ty_box, ty_field, ty_fn,
|
tt_tok, tt_nonterminal, ty, ty_, ty_bot, ty_box, ty_field, ty_fn,
|
||||||
ty_infer, ty_mac, ty_method, ty_nil, ty_param, ty_path, ty_ptr,
|
ty_infer, ty_mac, ty_method, ty_nil, ty_param, ty_path, ty_ptr,
|
||||||
ty_rec, ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec,
|
ty_rec, ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec,
|
||||||
ty_fixed_length, unchecked_blk, uniq, unsafe_blk, unsafe_fn,
|
ty_fixed_length, unchecked_blk, uniq, unsafe_blk, unsafe_fn,
|
||||||
|
@ -1640,6 +1640,52 @@ class parser {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_pat_fields(refutable: bool) -> (~[ast::field_pat], bool) {
|
||||||
|
let mut fields = ~[];
|
||||||
|
let mut etc = false;
|
||||||
|
let mut first = true;
|
||||||
|
while self.token != token::RBRACE {
|
||||||
|
if first { first = false; }
|
||||||
|
else { self.expect(token::COMMA); }
|
||||||
|
|
||||||
|
if self.token == token::UNDERSCORE {
|
||||||
|
self.bump();
|
||||||
|
if self.token != token::RBRACE {
|
||||||
|
self.fatal(~"expected `}`, found `" +
|
||||||
|
token_to_str(self.reader, self.token) +
|
||||||
|
~"`");
|
||||||
|
}
|
||||||
|
etc = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lo1 = self.last_span.lo;
|
||||||
|
let fieldname = if self.look_ahead(1u) == token::COLON {
|
||||||
|
self.parse_ident()
|
||||||
|
} else {
|
||||||
|
self.parse_value_ident()
|
||||||
|
};
|
||||||
|
let hi1 = self.last_span.lo;
|
||||||
|
let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1),
|
||||||
|
fieldname);
|
||||||
|
let mut subpat;
|
||||||
|
if self.token == token::COLON {
|
||||||
|
self.bump();
|
||||||
|
subpat = self.parse_pat(refutable);
|
||||||
|
} else {
|
||||||
|
subpat = @{
|
||||||
|
id: self.get_id(),
|
||||||
|
node: pat_ident(bind_by_implicit_ref,
|
||||||
|
fieldpath,
|
||||||
|
none),
|
||||||
|
span: self.last_span
|
||||||
|
};
|
||||||
|
}
|
||||||
|
vec::push(fields, {ident: fieldname, pat: subpat});
|
||||||
|
}
|
||||||
|
return (fields, etc);
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_pat(refutable: bool) -> @pat {
|
fn parse_pat(refutable: bool) -> @pat {
|
||||||
maybe_whole!{self, nt_pat};
|
maybe_whole!{self, nt_pat};
|
||||||
|
|
||||||
|
@ -1685,48 +1731,7 @@ class parser {
|
||||||
}
|
}
|
||||||
token::LBRACE => {
|
token::LBRACE => {
|
||||||
self.bump();
|
self.bump();
|
||||||
let mut fields = ~[];
|
let (fields, etc) = self.parse_pat_fields(refutable);
|
||||||
let mut etc = false;
|
|
||||||
let mut first = true;
|
|
||||||
while self.token != token::RBRACE {
|
|
||||||
if first { first = false; }
|
|
||||||
else { self.expect(token::COMMA); }
|
|
||||||
|
|
||||||
if self.token == token::UNDERSCORE {
|
|
||||||
self.bump();
|
|
||||||
if self.token != token::RBRACE {
|
|
||||||
self.fatal(~"expected `}`, found `" +
|
|
||||||
token_to_str(self.reader, self.token) +
|
|
||||||
~"`");
|
|
||||||
}
|
|
||||||
etc = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lo1 = self.last_span.lo;
|
|
||||||
let fieldname = if self.look_ahead(1u) == token::COLON {
|
|
||||||
self.parse_ident()
|
|
||||||
} else {
|
|
||||||
self.parse_value_ident()
|
|
||||||
};
|
|
||||||
let hi1 = self.last_span.lo;
|
|
||||||
let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1),
|
|
||||||
fieldname);
|
|
||||||
let mut subpat;
|
|
||||||
if self.token == token::COLON {
|
|
||||||
self.bump();
|
|
||||||
subpat = self.parse_pat(refutable);
|
|
||||||
} else {
|
|
||||||
subpat = @{
|
|
||||||
id: self.get_id(),
|
|
||||||
node: pat_ident(bind_by_implicit_ref,
|
|
||||||
fieldpath,
|
|
||||||
none),
|
|
||||||
span: mk_sp(lo, hi)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
vec::push(fields, {ident: fieldname, pat: subpat});
|
|
||||||
}
|
|
||||||
hi = self.span.hi;
|
hi = self.span.hi;
|
||||||
self.bump();
|
self.bump();
|
||||||
pat = pat_rec(fields, etc);
|
pat = pat_rec(fields, etc);
|
||||||
|
@ -1771,21 +1776,82 @@ class parser {
|
||||||
} else if !is_plain_ident(self.token) {
|
} else if !is_plain_ident(self.token) {
|
||||||
pat = self.parse_enum_variant(refutable);
|
pat = self.parse_enum_variant(refutable);
|
||||||
} else {
|
} else {
|
||||||
// this is a plain identifier, like `x` or `x(...)`
|
let binding_mode;
|
||||||
|
if self.eat_keyword(~"copy") {
|
||||||
|
binding_mode = bind_by_value;
|
||||||
|
} else if refutable {
|
||||||
|
// XXX: Should be bind_by_value, but that's not
|
||||||
|
// backward compatible.
|
||||||
|
binding_mode = bind_by_implicit_ref;
|
||||||
|
} else {
|
||||||
|
binding_mode = bind_by_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cannot_be_enum_or_struct;
|
||||||
match self.look_ahead(1) {
|
match self.look_ahead(1) {
|
||||||
token::LPAREN | token::LBRACKET | token::LT => {
|
token::LPAREN | token::LBRACKET | token::LT |
|
||||||
pat = self.parse_enum_variant(refutable);
|
token::LBRACE =>
|
||||||
}
|
cannot_be_enum_or_struct = false,
|
||||||
_ => {
|
_ =>
|
||||||
let binding_mode = if refutable {
|
cannot_be_enum_or_struct = true
|
||||||
// XXX: Should be bind_by_value, but that's not
|
}
|
||||||
// backward compatible.
|
|
||||||
bind_by_implicit_ref
|
if is_plain_ident(self.token) && cannot_be_enum_or_struct {
|
||||||
|
let name = self.parse_value_path();
|
||||||
|
let sub;
|
||||||
|
if self.eat(token::AT) {
|
||||||
|
sub = some(self.parse_pat(refutable));
|
||||||
} else {
|
} else {
|
||||||
bind_by_value
|
sub = none;
|
||||||
};
|
};
|
||||||
pat = self.parse_pat_ident(refutable, binding_mode);
|
pat = pat_ident(binding_mode, name, sub);
|
||||||
}
|
} else {
|
||||||
|
let enum_path = self.parse_path_with_tps(true);
|
||||||
|
match self.token {
|
||||||
|
token::LBRACE => {
|
||||||
|
self.bump();
|
||||||
|
let (fields, etc) =
|
||||||
|
self.parse_pat_fields(refutable);
|
||||||
|
self.bump();
|
||||||
|
pat = pat_struct(enum_path, fields, etc);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mut args: ~[@pat] = ~[];
|
||||||
|
let mut star_pat = false;
|
||||||
|
match self.token {
|
||||||
|
token::LPAREN => match self.look_ahead(1u) {
|
||||||
|
token::BINOP(token::STAR) => {
|
||||||
|
// This is a "top constructor only" pat
|
||||||
|
self.bump(); self.bump();
|
||||||
|
star_pat = true;
|
||||||
|
self.expect(token::RPAREN);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
args = self.parse_unspanned_seq(
|
||||||
|
token::LPAREN, token::RPAREN,
|
||||||
|
seq_sep_trailing_disallowed
|
||||||
|
(token::COMMA),
|
||||||
|
|p| p.parse_pat(refutable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
// at this point, we're not sure whether it's a
|
||||||
|
// enum or a bind
|
||||||
|
if star_pat {
|
||||||
|
pat = pat_enum(enum_path, none);
|
||||||
|
}
|
||||||
|
else if vec::is_empty(args) &&
|
||||||
|
vec::len(enum_path.idents) == 1u {
|
||||||
|
pat = pat_ident(binding_mode,
|
||||||
|
enum_path,
|
||||||
|
none);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pat = pat_enum(enum_path, some(args));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hi = self.span.hi;
|
hi = self.span.hi;
|
||||||
|
|
|
@ -1375,6 +1375,24 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
|
||||||
}
|
}
|
||||||
word(s.s, ~"}");
|
word(s.s, ~"}");
|
||||||
}
|
}
|
||||||
|
ast::pat_struct(path, fields, etc) => {
|
||||||
|
print_path(s, path, true);
|
||||||
|
word(s.s, ~"{");
|
||||||
|
fn print_field(s: ps, f: ast::field_pat) {
|
||||||
|
cbox(s, indent_unit);
|
||||||
|
word(s.s, *f.ident);
|
||||||
|
word_space(s, ~":");
|
||||||
|
print_pat(s, f.pat);
|
||||||
|
end(s);
|
||||||
|
}
|
||||||
|
fn get_span(f: ast::field_pat) -> codemap::span { return f.pat.span; }
|
||||||
|
commasep_cmnt(s, consistent, fields, print_field, get_span);
|
||||||
|
if etc {
|
||||||
|
if vec::len(fields) != 0u { word_space(s, ~","); }
|
||||||
|
word(s.s, ~"_");
|
||||||
|
}
|
||||||
|
word(s.s, ~"}");
|
||||||
|
}
|
||||||
ast::pat_tup(elts) => {
|
ast::pat_tup(elts) => {
|
||||||
popen(s);
|
popen(s);
|
||||||
commasep(s, inconsistent, elts, print_pat);
|
commasep(s, inconsistent, elts, print_pat);
|
||||||
|
|
|
@ -222,6 +222,12 @@ fn visit_pat<E>(p: @pat, e: E, v: vt<E>) {
|
||||||
pat_rec(fields, _) => for fields.each |f| {
|
pat_rec(fields, _) => for fields.each |f| {
|
||||||
v.visit_pat(f.pat, e, v)
|
v.visit_pat(f.pat, e, v)
|
||||||
}
|
}
|
||||||
|
pat_struct(path, fields, _) => {
|
||||||
|
visit_path(path, e, v);
|
||||||
|
for fields.each |f| {
|
||||||
|
v.visit_pat(f.pat, e, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
pat_tup(elts) => for elts.each |elt| {
|
pat_tup(elts) => for elts.each |elt| {
|
||||||
v.visit_pat(elt, e, v)
|
v.visit_pat(elt, e, v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -513,6 +513,14 @@ impl methods for gather_loan_ctxt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::pat_struct(_, field_pats, _) => {
|
||||||
|
// {f1: p1, ..., fN: pN}
|
||||||
|
for field_pats.each |fp| {
|
||||||
|
let cmt_field = self.bccx.cat_field(fp.pat, cmt, fp.ident);
|
||||||
|
self.gather_pat(cmt_field, fp.pat, arm_id, alt_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ast::pat_tup(subpats) => {
|
ast::pat_tup(subpats) => {
|
||||||
// (p1, ..., pN)
|
// (p1, ..., pN)
|
||||||
for subpats.each |subpat| {
|
for subpats.each |subpat| {
|
||||||
|
|
|
@ -212,7 +212,8 @@ fn pat_ctor_id(tcx: ty::ctxt, p: @pat) -> option<ctor> {
|
||||||
pat_range(lo, hi) => {
|
pat_range(lo, hi) => {
|
||||||
some(range(eval_const_expr(tcx, lo), eval_const_expr(tcx, hi)))
|
some(range(eval_const_expr(tcx, lo), eval_const_expr(tcx, hi)))
|
||||||
}
|
}
|
||||||
pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) => {
|
pat_box(_) | pat_uniq(_) | pat_rec(_, _) | pat_tup(_) |
|
||||||
|
pat_struct(*) => {
|
||||||
some(single)
|
some(single)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +235,8 @@ fn is_wild(tcx: ty::ctxt, p: @pat) -> bool {
|
||||||
|
|
||||||
fn missing_ctor(tcx: ty::ctxt, m: matrix, left_ty: ty::t) -> option<ctor> {
|
fn missing_ctor(tcx: ty::ctxt, m: matrix, left_ty: ty::t) -> option<ctor> {
|
||||||
match ty::get(left_ty).struct {
|
match ty::get(left_ty).struct {
|
||||||
ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_tup(_) | ty::ty_rec(_) => {
|
ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_tup(_) | ty::ty_rec(_) |
|
||||||
|
ty::ty_class(*) => {
|
||||||
for m.each |r| {
|
for m.each |r| {
|
||||||
if !is_wild(tcx, r[0]) { return none; }
|
if !is_wild(tcx, r[0]) { return none; }
|
||||||
}
|
}
|
||||||
|
@ -286,6 +288,7 @@ fn ctor_arity(tcx: ty::ctxt, ctor: ctor, ty: ty::t) -> uint {
|
||||||
some(v) => v.args.len()
|
some(v) => v.args.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ty::ty_class(cid, _) => ty::lookup_class_fields(tcx, cid).len(),
|
||||||
_ => 0u
|
_ => 0u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,7 +330,29 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint,
|
||||||
};
|
};
|
||||||
let args = vec::map(ty_flds, |ty_f| {
|
let args = vec::map(ty_flds, |ty_f| {
|
||||||
match vec::find(flds, |f| f.ident == ty_f.ident ) {
|
match vec::find(flds, |f| f.ident == ty_f.ident ) {
|
||||||
some(f) => f.pat, _ => wild()
|
some(f) => f.pat,
|
||||||
|
_ => wild()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
some(vec::append(args, vec::tail(r)))
|
||||||
|
}
|
||||||
|
pat_struct(_, flds, _) => {
|
||||||
|
// Grab the class data that we care about.
|
||||||
|
let class_fields, class_id;
|
||||||
|
match ty::get(left_ty).struct {
|
||||||
|
ty::ty_class(cid, substs) => {
|
||||||
|
class_id = cid;
|
||||||
|
class_fields = ty::lookup_class_fields(tcx, class_id);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tcx.sess.span_bug(r0.span, ~"struct pattern didn't resolve \
|
||||||
|
to a struct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let args = vec::map(class_fields, |class_field| {
|
||||||
|
match vec::find(flds, |f| f.ident == class_field.ident ) {
|
||||||
|
some(f) => f.pat,
|
||||||
|
_ => wild()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
some(vec::append(args, vec::tail(r)))
|
some(vec::append(args, vec::tail(r)))
|
||||||
|
@ -377,7 +402,9 @@ fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) {
|
||||||
fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
|
fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
|
||||||
match tcx.def_map.find(pat.id) {
|
match tcx.def_map.find(pat.id) {
|
||||||
some(def_variant(enum_id, var_id)) => {
|
some(def_variant(enum_id, var_id)) => {
|
||||||
if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u { return true; }
|
if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
@ -394,6 +421,12 @@ fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
pat_struct(_, fields, _) => {
|
||||||
|
for fields.each |it| {
|
||||||
|
if is_refutable(tcx, it.pat) { return true; }
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
pat_tup(elts) => {
|
pat_tup(elts) => {
|
||||||
for elts.each |elt| { if is_refutable(tcx, elt) { return true; } }
|
for elts.each |elt| { if is_refutable(tcx, elt) { return true; } }
|
||||||
false
|
false
|
||||||
|
|
|
@ -15,8 +15,8 @@ import syntax::ast::{bound_trait, binding_mode,
|
||||||
import syntax::ast::{class_member, class_method, crate, crate_num, decl_item};
|
import syntax::ast::{class_member, class_method, crate, crate_num, decl_item};
|
||||||
import syntax::ast::{def, def_arg, def_binding, def_class, def_const, def_fn};
|
import syntax::ast::{def, def_arg, def_binding, def_class, def_const, def_fn};
|
||||||
import syntax::ast::{def_foreign_mod, def_id, def_local, def_mod};
|
import syntax::ast::{def_foreign_mod, def_id, def_local, def_mod};
|
||||||
import syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param,
|
import syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param};
|
||||||
def_typaram_binder};
|
import syntax::ast::{def_typaram_binder};
|
||||||
import syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op};
|
import syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op};
|
||||||
import syntax::ast::{expr_binary, expr_cast, expr_field, expr_fn};
|
import syntax::ast::{expr_binary, expr_cast, expr_field, expr_fn};
|
||||||
import syntax::ast::{expr_fn_block, expr_index, expr_path};
|
import syntax::ast::{expr_fn_block, expr_index, expr_path};
|
||||||
|
@ -30,13 +30,13 @@ import syntax::ast::{instance_var, item, item_class, item_const, item_enum};
|
||||||
import syntax::ast::{item_fn, item_mac, item_foreign_mod, item_impl};
|
import syntax::ast::{item_fn, item_mac, item_foreign_mod, item_impl};
|
||||||
import syntax::ast::{item_mod, item_trait, item_ty, le, local, local_crate};
|
import syntax::ast::{item_mod, item_trait, item_ty, le, local, local_crate};
|
||||||
import syntax::ast::{lt, method, mul, ne, neg, node_id, pat, pat_enum};
|
import syntax::ast::{lt, method, mul, ne, neg, node_id, pat, pat_enum};
|
||||||
import syntax::ast::{pat_ident, path, prim_ty, pat_box, pat_uniq, pat_lit};
|
import syntax::ast::{pat_ident, pat_struct, path, prim_ty, pat_box, pat_uniq};
|
||||||
import syntax::ast::{pat_range, pat_rec, pat_tup, pat_wild, provided};
|
import syntax::ast::{pat_lit, pat_range, pat_rec, pat_tup, pat_wild};
|
||||||
import syntax::ast::{required, rem, self_ty_, shl, stmt_decl, subtract, ty};
|
import syntax::ast::{provided, required, rem, self_ty_, shl, stmt_decl};
|
||||||
import syntax::ast::{ty_bool, ty_char, ty_f, ty_f32, ty_f64, ty_float, ty_i};
|
import syntax::ast::{subtract, ty, ty_bool, ty_char, ty_f, ty_f32, ty_f64};
|
||||||
import syntax::ast::{ty_i16, ty_i32, ty_i64, ty_i8, ty_int, ty_param};
|
import syntax::ast::{ty_float, ty_i, ty_i16, ty_i32, ty_i64, ty_i8, ty_int};
|
||||||
import syntax::ast::{ty_path, ty_str, ty_u, ty_u16, ty_u32, ty_u64, ty_u8};
|
import syntax::ast::{ty_param, ty_path, ty_str, ty_u, ty_u16, ty_u32, ty_u64};
|
||||||
import syntax::ast::{ty_uint, variant, view_item, view_item_export};
|
import syntax::ast::{ty_u8, ty_uint, variant, view_item, view_item_export};
|
||||||
import syntax::ast::{view_item_import, view_item_use, view_path_glob};
|
import syntax::ast::{view_item_import, view_item_use, view_path_glob};
|
||||||
import syntax::ast::{view_path_list, view_path_simple};
|
import syntax::ast::{view_path_list, view_path_simple};
|
||||||
import syntax::ast_util::{def_id_of_def, dummy_sp, local_def, new_def_hash};
|
import syntax::ast_util::{def_id_of_def, dummy_sp, local_def, new_def_hash};
|
||||||
|
@ -4017,6 +4017,26 @@ class Resolver {
|
||||||
self.resolve_expr(last_expr, visitor);
|
self.resolve_expr(last_expr, visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pat_struct(path, _, _) => {
|
||||||
|
match self.resolve_path(path, TypeNS, false, visitor) {
|
||||||
|
some(definition @ def_ty(class_id))
|
||||||
|
if self.structs.contains_key(class_id) => {
|
||||||
|
let has_constructor = self.structs.get(class_id);
|
||||||
|
let class_def = def_class(class_id,
|
||||||
|
has_constructor);
|
||||||
|
self.record_def(pattern.id, class_def);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.session.span_err(path.span,
|
||||||
|
fmt!("`%s` does not name a \
|
||||||
|
structure",
|
||||||
|
connect(path.idents.map
|
||||||
|
(|x| *x),
|
||||||
|
~"::")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,8 @@ fn enter_default(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef)
|
||||||
|
|
||||||
do enter_match(bcx, dm, m, col, val) |p| {
|
do enter_match(bcx, dm, m, col, val) |p| {
|
||||||
match p.node {
|
match p.node {
|
||||||
ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) => some(~[]),
|
ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) |
|
||||||
|
ast::pat_struct(*) => some(~[]),
|
||||||
ast::pat_ident(_, _, none) if !pat_is_variant(dm, p) => some(~[]),
|
ast::pat_ident(_, _, none) if !pat_is_variant(dm, p) => some(~[]),
|
||||||
_ => none
|
_ => none
|
||||||
}
|
}
|
||||||
|
@ -221,12 +222,12 @@ fn enter_opt(bcx: block, m: match_, opt: opt, col: uint,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_rec(bcx: block, dm: DefMap, m: match_, col: uint,
|
fn enter_rec_or_struct(bcx: block, dm: DefMap, m: match_, col: uint,
|
||||||
fields: ~[ast::ident], val: ValueRef) -> match_ {
|
fields: ~[ast::ident], val: ValueRef) -> match_ {
|
||||||
let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
|
let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
|
||||||
do enter_match(bcx, dm, m, col, val) |p| {
|
do enter_match(bcx, dm, m, col, val) |p| {
|
||||||
match p.node {
|
match p.node {
|
||||||
ast::pat_rec(fpats, _) => {
|
ast::pat_rec(fpats, _) | ast::pat_struct(_, fpats, _) => {
|
||||||
let mut pats = ~[];
|
let mut pats = ~[];
|
||||||
for vec::each(fields) |fname| {
|
for vec::each(fields) |fname| {
|
||||||
let mut pat = dummy;
|
let mut pat = dummy;
|
||||||
|
@ -344,6 +345,23 @@ fn collect_record_fields(m: match_, col: uint) -> ~[ast::ident] {
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_struct_fields(m: match_, col: uint) -> ~[ast::ident] {
|
||||||
|
let mut fields: ~[ast::ident] = ~[];
|
||||||
|
for vec::each(m) |br| {
|
||||||
|
match br.pats[col].node {
|
||||||
|
ast::pat_struct(_, fs, _) => {
|
||||||
|
for vec::each(fs) |f| {
|
||||||
|
if !vec::any(fields, |x| str::eq(f.ident, x)) {
|
||||||
|
vec::push(fields, f.ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
fn root_pats_as_necessary(bcx: block, m: match_, col: uint, val: ValueRef) {
|
fn root_pats_as_necessary(bcx: block, m: match_, col: uint, val: ValueRef) {
|
||||||
for vec::each(m) |br| {
|
for vec::each(m) |br| {
|
||||||
let pat_id = br.pats[col].id;
|
let pat_id = br.pats[col].id;
|
||||||
|
@ -500,15 +518,56 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
|
||||||
|
|
||||||
let rec_fields = collect_record_fields(m, col);
|
let rec_fields = collect_record_fields(m, col);
|
||||||
// Separate path for extracting and binding record fields
|
// Separate path for extracting and binding record fields
|
||||||
if rec_fields.len() > 0u {
|
if rec_fields.len() > 0 {
|
||||||
let fields = ty::get_fields(node_id_type(bcx, pat_id));
|
let fields = ty::get_fields(node_id_type(bcx, pat_id));
|
||||||
let mut rec_vals = ~[];
|
let mut rec_vals = ~[];
|
||||||
for vec::each(rec_fields) |field_name| {
|
for vec::each(rec_fields) |field_name| {
|
||||||
let ix = option::get(ty::field_idx(field_name, fields));
|
let ix = option::get(ty::field_idx(field_name, fields));
|
||||||
vec::push(rec_vals, GEPi(bcx, val, ~[0u, ix]));
|
vec::push(rec_vals, GEPi(bcx, val, ~[0u, ix]));
|
||||||
}
|
}
|
||||||
compile_submatch(bcx, enter_rec(bcx, dm, m, col, rec_fields, val),
|
compile_submatch(bcx,
|
||||||
vec::append(rec_vals, vals_left), chk, exits);
|
enter_rec_or_struct(bcx, dm, m, col, rec_fields,
|
||||||
|
val),
|
||||||
|
vec::append(rec_vals, vals_left),
|
||||||
|
chk,
|
||||||
|
exits);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separate path for extracting and binding struct fields.
|
||||||
|
let struct_fields = collect_struct_fields(m, col);
|
||||||
|
if struct_fields.len() > 0 {
|
||||||
|
let class_id, class_fields;
|
||||||
|
match ty::get(node_id_type(bcx, pat_id)).struct {
|
||||||
|
ty::ty_class(cid, _) => {
|
||||||
|
class_id = cid;
|
||||||
|
class_fields = ty::lookup_class_fields(ccx.tcx, class_id);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
ccx.tcx.sess.bug(~"struct pattern didn't resolve to a \
|
||||||
|
struct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index the class fields.
|
||||||
|
let field_map = std::map::box_str_hash();
|
||||||
|
for class_fields.eachi |i, class_field| {
|
||||||
|
field_map.insert(class_field.ident, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch each field.
|
||||||
|
let mut struct_vals = ~[];
|
||||||
|
for struct_fields.each |field_name| {
|
||||||
|
let index = field_map.get(field_name);
|
||||||
|
let fldptr = base::get_struct_field(bcx, val, class_id, index);
|
||||||
|
vec::push(struct_vals, fldptr);
|
||||||
|
}
|
||||||
|
compile_submatch(bcx,
|
||||||
|
enter_rec_or_struct(bcx, dm, m, col, struct_fields,
|
||||||
|
val),
|
||||||
|
vec::append(struct_vals, vals_left),
|
||||||
|
chk,
|
||||||
|
exits);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -877,6 +936,34 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
|
||||||
bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy);
|
bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ast::pat_struct(_, fields, _) => {
|
||||||
|
// Grab the class data that we care about.
|
||||||
|
let class_fields, class_id;
|
||||||
|
match ty::get(node_id_type(bcx, pat.id)).struct {
|
||||||
|
ty::ty_class(cid, substs) => {
|
||||||
|
class_id = cid;
|
||||||
|
class_fields = ty::lookup_class_fields(ccx.tcx, class_id);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
ccx.tcx.sess.span_bug(pat.span, ~"struct pattern didn't \
|
||||||
|
resolve to a struct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index the class fields.
|
||||||
|
let field_map = std::map::box_str_hash();
|
||||||
|
for class_fields.eachi |i, class_field| {
|
||||||
|
field_map.insert(class_field.ident, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch each field.
|
||||||
|
for fields.each |supplied_field| {
|
||||||
|
let index = field_map.get(supplied_field.ident);
|
||||||
|
let fldptr = base::get_struct_field(bcx, val, class_id, index);
|
||||||
|
bcx = bind_irrefutable_pat(bcx, supplied_field.pat, fldptr,
|
||||||
|
make_copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
ast::pat_tup(elems) => {
|
ast::pat_tup(elems) => {
|
||||||
let mut i = 0u;
|
let mut i = 0u;
|
||||||
for vec::each(elems) |elem| {
|
for vec::each(elems) |elem| {
|
||||||
|
|
|
@ -3391,6 +3391,18 @@ fn trans_rec(bcx: block, fields: ~[ast::field],
|
||||||
return bcx;
|
return bcx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the class has a destructor, our GEP is a little more
|
||||||
|
// complicated.
|
||||||
|
fn get_struct_field(block_context: block, dest_address: ValueRef,
|
||||||
|
class_id: ast::def_id, index: uint) -> ValueRef {
|
||||||
|
if ty::ty_dtor(block_context.tcx(), class_id).is_some() {
|
||||||
|
return GEPi(block_context,
|
||||||
|
GEPi(block_context, dest_address, ~[0, 1]),
|
||||||
|
~[0, index]);
|
||||||
|
}
|
||||||
|
return GEPi(block_context, dest_address, ~[0, index]);
|
||||||
|
}
|
||||||
|
|
||||||
fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
|
fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
|
||||||
base: option<@ast::expr>, id: ast::node_id, dest: dest)
|
base: option<@ast::expr>, id: ast::node_id, dest: dest)
|
||||||
-> block {
|
-> block {
|
||||||
|
@ -3434,18 +3446,6 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the class has a destructor, our GEP is a little more
|
|
||||||
// complicated.
|
|
||||||
fn get_field(block_context: block, dest_address: ValueRef,
|
|
||||||
class_id: ast::def_id, index: uint) -> ValueRef {
|
|
||||||
if ty::ty_dtor(block_context.tcx(), class_id).is_some() {
|
|
||||||
return GEPi(block_context,
|
|
||||||
GEPi(block_context, dest_address, ~[0, 1]),
|
|
||||||
~[0, index]);
|
|
||||||
}
|
|
||||||
return GEPi(block_context, dest_address, ~[0, index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now translate each field.
|
// Now translate each field.
|
||||||
let mut temp_cleanups = ~[];
|
let mut temp_cleanups = ~[];
|
||||||
for fields.each |field| {
|
for fields.each |field| {
|
||||||
|
@ -3468,7 +3468,8 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dest = get_field(block_context, dest_address, class_id, index);
|
let dest = get_struct_field(block_context, dest_address, class_id,
|
||||||
|
index);
|
||||||
|
|
||||||
block_context = trans_expr_save_in(block_context,
|
block_context = trans_expr_save_in(block_context,
|
||||||
field.node.expr,
|
field.node.expr,
|
||||||
|
@ -3494,10 +3495,10 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
|
||||||
if exists {
|
if exists {
|
||||||
again;
|
again;
|
||||||
}
|
}
|
||||||
let lldestfieldvalue = get_field(block_context,
|
let lldestfieldvalue = get_struct_field(block_context,
|
||||||
dest_address,
|
dest_address,
|
||||||
class_id,
|
class_id,
|
||||||
i);
|
i);
|
||||||
let llbasefieldvalue = GEPi(block_context,
|
let llbasefieldvalue = GEPi(block_context,
|
||||||
llbasevalue,
|
llbasevalue,
|
||||||
~[0, i]);
|
~[0, i]);
|
||||||
|
|
|
@ -232,6 +232,86 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
|
||||||
}
|
}
|
||||||
fcx.write_ty(pat.id, expected);
|
fcx.write_ty(pat.id, expected);
|
||||||
}
|
}
|
||||||
|
ast::pat_struct(path, fields, etc) => {
|
||||||
|
// Grab the class data that we care about.
|
||||||
|
let class_fields, class_id, substitutions;
|
||||||
|
match structure_of(fcx, pat.span, expected) {
|
||||||
|
ty::ty_class(cid, substs) => {
|
||||||
|
class_id = cid;
|
||||||
|
substitutions = substs;
|
||||||
|
class_fields = ty::lookup_class_fields(tcx, class_id);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// XXX: This should not be fatal.
|
||||||
|
tcx.sess.span_fatal(pat.span,
|
||||||
|
fmt!("mismatched types: expected `%s` \
|
||||||
|
but found struct",
|
||||||
|
fcx.infcx.ty_to_str(expected)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to ensure that the struct is the one specified.
|
||||||
|
match tcx.def_map.get(pat.id) {
|
||||||
|
ast::def_class(supplied_def_id, _)
|
||||||
|
if supplied_def_id == class_id => {
|
||||||
|
// OK.
|
||||||
|
}
|
||||||
|
ast::def_class(*) => {
|
||||||
|
let name = syntax::print::pprust::path_to_str(path);
|
||||||
|
tcx.sess.span_err(pat.span,
|
||||||
|
fmt!("mismatched types: expected `%s` but \
|
||||||
|
found `%s`",
|
||||||
|
fcx.infcx.ty_to_str(expected),
|
||||||
|
name));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tcx.sess.span_bug(pat.span, ~"resolve didn't write in class");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index the class fields.
|
||||||
|
let field_map = std::map::box_str_hash();
|
||||||
|
for class_fields.eachi |i, class_field| {
|
||||||
|
field_map.insert(class_field.ident, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Typecheck each field.
|
||||||
|
let found_fields = std::map::uint_hash();
|
||||||
|
for fields.each |field| {
|
||||||
|
match field_map.find(field.ident) {
|
||||||
|
some(index) => {
|
||||||
|
let class_field = class_fields[index];
|
||||||
|
let field_type = ty::lookup_field_type(tcx,
|
||||||
|
class_id,
|
||||||
|
class_field.id,
|
||||||
|
substitutions);
|
||||||
|
check_pat(pcx, field.pat, field_type);
|
||||||
|
found_fields.insert(index, ());
|
||||||
|
}
|
||||||
|
none => {
|
||||||
|
let name = syntax::print::pprust::path_to_str(path);
|
||||||
|
tcx.sess.span_err(pat.span,
|
||||||
|
fmt!("struct `%s` does not have a field
|
||||||
|
named `%s`", name, *field.ident));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report an error if not all the fields were specified.
|
||||||
|
if !etc {
|
||||||
|
for class_fields.eachi |i, field| {
|
||||||
|
if found_fields.contains_key(i) {
|
||||||
|
again;
|
||||||
|
}
|
||||||
|
tcx.sess.span_err(pat.span,
|
||||||
|
fmt!("pattern does not mention field `%s`",
|
||||||
|
*field.ident));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, write in the type.
|
||||||
|
fcx.write_ty(pat.id, expected);
|
||||||
|
}
|
||||||
ast::pat_tup(elts) => {
|
ast::pat_tup(elts) => {
|
||||||
let ex_elts = match structure_of(fcx, pat.span, expected) {
|
let ex_elts = match structure_of(fcx, pat.span, expected) {
|
||||||
ty::ty_tup(elts) => elts,
|
ty::ty_tup(elts) => elts,
|
||||||
|
|
14
src/test/run-pass/struct-pattern-matching.rs
Normal file
14
src/test/run-pass/struct-pattern-matching.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
struct Foo {
|
||||||
|
x: int;
|
||||||
|
y: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = Foo { x: 1, y: 2 };
|
||||||
|
match a {
|
||||||
|
Foo { x: x, y: y } => io::println(fmt!("yes, %d, %d", x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue