c3bc8fada8
This commit allows patterns like: alt x { some(_) { ... } none { } } without the '.' after none. The parser suspends judgment about whether a bare ident is a tag or a new bound variable; instead, the resolver disambiguates. This means that any code after resolution that pattern-matches on patterns needs to call pat_util::normalize_pat, which consults an environment to do this disambiguation. In addition, local variables are no longer allowed to shadow tag names, so this required changing some code (e.g. renaming variables named "mut", and renaming ast::sub to subtract). The parser currently accepts patterns with and without the '.'. Once the compiler and libraries are changed, it will no longer accept the '.'.
173 lines
4.9 KiB
Rust
173 lines
4.9 KiB
Rust
import syntax::ast::*;
|
|
import syntax::ast_util::{variant_def_ids, dummy_sp, compare_lit_exprs,
|
|
lit_expr_eq};
|
|
import pat_util::*;
|
|
import syntax::visit;
|
|
import option::{some, none};
|
|
import driver::session::session;
|
|
|
|
fn check_crate(tcx: ty::ctxt, crate: @crate) {
|
|
let v =
|
|
@{visit_expr: bind check_expr(tcx, _, _, _),
|
|
visit_local: bind check_local(tcx, _, _, _)
|
|
with *visit::default_visitor::<()>()};
|
|
visit::visit_crate(*crate, (), visit::mk_vt(v));
|
|
tcx.sess.abort_if_errors();
|
|
}
|
|
|
|
fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) {
|
|
visit::visit_expr(ex, s, v);
|
|
alt ex.node {
|
|
expr_alt(_, arms) {
|
|
check_arms(tcx, pat_util::normalize_arms(tcx, arms));
|
|
}
|
|
_ { }
|
|
}
|
|
}
|
|
|
|
fn check_arms(tcx: ty::ctxt, arms: [arm]) {
|
|
let i = 0;
|
|
for arm: arm in arms {
|
|
for arm_pat: @pat in arm.pats {
|
|
let reachable = true;
|
|
let j = 0;
|
|
while j < i {
|
|
if option::is_none(arms[j].guard) {
|
|
for prev_pat: @pat in arms[j].pats {
|
|
if pattern_supersedes(tcx, prev_pat, arm_pat) {
|
|
reachable = false;
|
|
}
|
|
}
|
|
}
|
|
j += 1;
|
|
}
|
|
if !reachable {
|
|
tcx.sess.span_err(arm_pat.span, "unreachable pattern");
|
|
}
|
|
}
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
|
|
fn patterns_supersede(tcx: ty::ctxt, as: [@pat], bs: [@pat]) -> bool {
|
|
let i = 0;
|
|
for a: @pat in as {
|
|
if !pattern_supersedes(tcx, a, bs[i]) { ret false; }
|
|
i += 1;
|
|
}
|
|
ret true;
|
|
}
|
|
fn field_patterns_supersede(tcx: ty::ctxt, fas: [field_pat],
|
|
fbs: [field_pat]) -> bool {
|
|
let wild = @{id: 0, node: pat_wild, span: dummy_sp()};
|
|
for fa: field_pat in fas {
|
|
let pb = wild;
|
|
for fb: field_pat in fbs {
|
|
if fa.ident == fb.ident { pb = fb.pat; }
|
|
}
|
|
if !pattern_supersedes(tcx, fa.pat, pb) { ret false; }
|
|
}
|
|
ret true;
|
|
}
|
|
|
|
alt a.node {
|
|
pat_ident(_, some(p)) { pattern_supersedes(tcx, p, b) }
|
|
pat_wild. | pat_ident(_, none.) { true }
|
|
pat_lit(la) {
|
|
alt b.node {
|
|
pat_lit(lb) { lit_expr_eq(la, lb) }
|
|
_ { false }
|
|
}
|
|
}
|
|
pat_tag(va, suba) {
|
|
alt b.node {
|
|
pat_tag(vb, subb) {
|
|
tcx.def_map.get(a.id) == tcx.def_map.get(b.id) &&
|
|
patterns_supersede(tcx, suba, subb)
|
|
}
|
|
_ { false }
|
|
}
|
|
}
|
|
pat_rec(suba, _) {
|
|
alt b.node {
|
|
pat_rec(subb, _) { field_patterns_supersede(tcx, suba, subb) }
|
|
_ { false }
|
|
}
|
|
}
|
|
pat_tup(suba) {
|
|
alt b.node {
|
|
pat_tup(subb) { patterns_supersede(tcx, suba, subb) }
|
|
_ { false }
|
|
}
|
|
}
|
|
pat_box(suba) {
|
|
alt b.node {
|
|
pat_box(subb) { pattern_supersedes(tcx, suba, subb) }
|
|
_ { pattern_supersedes(tcx, suba, b) }
|
|
}
|
|
}
|
|
pat_uniq(suba) {
|
|
alt b.node {
|
|
pat_uniq(subb) { pattern_supersedes(tcx, suba, subb) }
|
|
_ { pattern_supersedes(tcx, suba, b) }
|
|
}
|
|
}
|
|
pat_range(begina, enda) {
|
|
alt b.node {
|
|
pat_lit(lb) {
|
|
compare_lit_exprs(begina, lb) <= 0 &&
|
|
compare_lit_exprs(enda, lb) >= 0
|
|
}
|
|
pat_range(beginb, endb) {
|
|
compare_lit_exprs(begina, beginb) <= 0 &&
|
|
compare_lit_exprs(enda, endb) >= 0
|
|
}
|
|
_ { false }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) {
|
|
visit::visit_local(loc, s, v);
|
|
if is_refutable(tcx, loc.node.pat) {
|
|
tcx.sess.span_err(loc.node.pat.span,
|
|
"refutable pattern in local binding");
|
|
}
|
|
}
|
|
|
|
fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
|
|
alt normalize_pat(tcx, pat).node {
|
|
pat_box(sub) | pat_uniq(sub) | pat_ident(_, some(sub)) {
|
|
is_refutable(tcx, sub)
|
|
}
|
|
pat_wild. | pat_ident(_, none.) { false }
|
|
pat_lit(_) { true }
|
|
pat_rec(fields, _) {
|
|
for field: field_pat in fields {
|
|
if is_refutable(tcx, field.pat) { ret true; }
|
|
}
|
|
false
|
|
}
|
|
pat_tup(elts) {
|
|
for elt in elts { if is_refutable(tcx, elt) { ret true; } }
|
|
false
|
|
}
|
|
pat_tag(_, args) {
|
|
let vdef = variant_def_ids(tcx.def_map.get(pat.id));
|
|
if vec::len(*ty::tag_variants(tcx, vdef.tg)) != 1u { ret true; }
|
|
for p: @pat in args { if is_refutable(tcx, p) { ret true; } }
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Local Variables:
|
|
// mode: rust
|
|
// fill-column: 78;
|
|
// indent-tabs-mode: nil
|
|
// c-basic-offset: 4
|
|
// buffer-file-coding-system: utf-8-unix
|
|
// End:
|