2012-01-30 23:00:57 -06:00
|
|
|
|
2011-07-25 06:45:09 -05:00
|
|
|
import syntax::ast::*;
|
2011-12-02 06:42:51 -06:00
|
|
|
import syntax::ast_util::{variant_def_ids, dummy_sp, compare_lit_exprs,
|
2012-01-30 23:00:57 -06:00
|
|
|
lit_expr_eq, unguarded_pat};
|
|
|
|
import syntax::codemap::span;
|
2012-01-14 18:05:07 -06:00
|
|
|
import pat_util::*;
|
2011-07-25 06:45:09 -05:00
|
|
|
import syntax::visit;
|
2012-01-12 10:59:49 -06:00
|
|
|
import driver::session::session;
|
2012-01-30 23:00:57 -06:00
|
|
|
import middle::ty;
|
|
|
|
import middle::ty::*;
|
2011-07-25 06:45:09 -05:00
|
|
|
|
2011-09-12 04:27:30 -05:00
|
|
|
fn check_crate(tcx: ty::ctxt, crate: @crate) {
|
2011-07-27 07:19:39 -05:00
|
|
|
let v =
|
2011-08-01 08:26:48 -05:00
|
|
|
@{visit_expr: bind check_expr(tcx, _, _, _),
|
|
|
|
visit_local: bind check_local(tcx, _, _, _)
|
2011-08-19 17:16:48 -05:00
|
|
|
with *visit::default_visitor::<()>()};
|
2011-07-25 06:45:09 -05:00
|
|
|
visit::visit_crate(*crate, (), visit::mk_vt(v));
|
|
|
|
tcx.sess.abort_if_errors();
|
|
|
|
}
|
|
|
|
|
2011-10-06 05:26:12 -05:00
|
|
|
fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) {
|
2011-07-25 06:45:09 -05:00
|
|
|
visit::visit_expr(ex, s, v);
|
2012-01-14 18:05:07 -06:00
|
|
|
alt ex.node {
|
2012-02-15 02:35:11 -06:00
|
|
|
expr_alt(scrut, arms, mode) {
|
|
|
|
let arms = pat_util::normalize_arms(tcx, arms);
|
|
|
|
check_arms(tcx, arms);
|
|
|
|
/* Check for exhaustiveness */
|
|
|
|
if mode == alt_exhaustive {
|
|
|
|
let arms = vec::concat(vec::filter_map(arms, unguarded_pat));
|
|
|
|
check_exhaustive(tcx, ex.span, expr_ty(tcx, scrut), arms);
|
2012-01-14 18:05:07 -06:00
|
|
|
}
|
2012-02-15 02:35:11 -06:00
|
|
|
}
|
|
|
|
_ { }
|
2012-01-14 18:05:07 -06:00
|
|
|
}
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
|
2012-02-15 02:35:11 -06:00
|
|
|
fn check_arms(tcx: ty::ctxt, arms: [arm]) {
|
2011-07-27 07:19:39 -05:00
|
|
|
let i = 0;
|
2012-01-30 23:00:57 -06:00
|
|
|
/* Check for unreachable patterns */
|
2011-08-15 23:54:52 -05:00
|
|
|
for arm: arm in arms {
|
|
|
|
for arm_pat: @pat in arm.pats {
|
2011-07-27 07:19:39 -05:00
|
|
|
let reachable = true;
|
|
|
|
let j = 0;
|
2011-07-25 06:45:09 -05:00
|
|
|
while j < i {
|
2011-12-13 18:25:51 -06:00
|
|
|
if option::is_none(arms[j].guard) {
|
2011-08-22 07:38:48 -05:00
|
|
|
for prev_pat: @pat in arms[j].pats {
|
|
|
|
if pattern_supersedes(tcx, prev_pat, arm_pat) {
|
|
|
|
reachable = false;
|
|
|
|
}
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
j += 1;
|
|
|
|
}
|
|
|
|
if !reachable {
|
2011-09-02 17:34:58 -05:00
|
|
|
tcx.sess.span_err(arm_pat.span, "unreachable pattern");
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
2012-01-30 23:00:57 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Precondition: patterns have been normalized
|
|
|
|
// (not checked statically yet)
|
|
|
|
fn check_exhaustive(tcx: ty::ctxt, sp:span, scrut_ty:ty::t, pats:[@pat]) {
|
|
|
|
let represented : [def_id] = [];
|
|
|
|
/* Determine the type of the scrutinee */
|
|
|
|
/* If it's not an enum, exit (bailing out on checking non-enum alts
|
|
|
|
for now) */
|
|
|
|
/* Otherwise, get the list of variants and make sure each one is
|
|
|
|
represented. Then recurse on the columns. */
|
|
|
|
|
2012-02-03 08:15:28 -06:00
|
|
|
let ty_def_id = alt ty::get(scrut_ty).struct {
|
2012-01-30 23:00:57 -06:00
|
|
|
ty_enum(id, _) { id }
|
|
|
|
_ { ret; } };
|
|
|
|
|
|
|
|
let variants = *enum_variants(tcx, ty_def_id);
|
|
|
|
for pat in pats {
|
|
|
|
if !is_refutable(tcx, pat) {
|
|
|
|
/* automatically makes this alt complete */ ret;
|
|
|
|
}
|
|
|
|
alt pat.node {
|
|
|
|
// want the def_id for the constructor
|
|
|
|
pat_enum(id,_) {
|
|
|
|
alt tcx.def_map.find(pat.id) {
|
|
|
|
some(def_variant(_, variant_def_id)) {
|
|
|
|
represented += [variant_def_id];
|
|
|
|
}
|
|
|
|
_ { tcx.sess.span_bug(pat.span, "check_exhaustive:
|
|
|
|
pat_tag not bound to a variant"); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ { tcx.sess.span_bug(pat.span, "check_exhaustive: ill-typed \
|
|
|
|
pattern"); // we know this has enum type,
|
|
|
|
} // so anything else should be impossible
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn not_represented(v: [def_id], &&vinfo: variant_info) -> bool {
|
2012-02-11 19:54:49 -06:00
|
|
|
!vec::contains(v, vinfo.id)
|
2012-01-30 23:00:57 -06:00
|
|
|
}
|
|
|
|
// Could be more efficient (bitvectors?)
|
|
|
|
alt vec::find(variants, bind not_represented(represented,_)) {
|
|
|
|
some(bad) {
|
|
|
|
// complain
|
|
|
|
// TODO: give examples of cases that aren't covered
|
|
|
|
tcx.sess.note("Patterns not covered include:");
|
|
|
|
tcx.sess.note(bad.name);
|
|
|
|
tcx.sess.span_err(sp, "Non-exhaustive pattern");
|
|
|
|
}
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
// Otherwise, check subpatterns
|
|
|
|
// inefficient
|
|
|
|
for variant in variants {
|
|
|
|
// rows consists of the argument list for each pat that's an enum
|
|
|
|
let rows : [[@pat]] = [];
|
|
|
|
for pat in pats {
|
|
|
|
alt pat.node {
|
|
|
|
pat_enum(id, args) {
|
|
|
|
alt tcx.def_map.find(pat.id) {
|
|
|
|
some(def_variant(_,variant_id))
|
|
|
|
if variant_id == variant.id { rows += [args]; }
|
|
|
|
_ { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if check vec::is_not_empty(rows) {
|
|
|
|
let i = 0u;
|
|
|
|
for it in rows[0] {
|
|
|
|
let column = [it];
|
|
|
|
// Annoying -- see comment in
|
|
|
|
// tstate::states::find_pre_post_state_loop
|
|
|
|
check vec::is_not_empty(rows);
|
|
|
|
for row in vec::tail(rows) {
|
|
|
|
column += [row[i]];
|
|
|
|
}
|
|
|
|
check_exhaustive(tcx, sp, pat_ty(tcx, it), column);
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// This shouldn't actually happen, since there were no
|
|
|
|
// irrefutable patterns if we got here.
|
|
|
|
else { cont; }
|
|
|
|
}
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
|
2011-09-12 04:27:30 -05:00
|
|
|
fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
|
|
|
|
fn patterns_supersede(tcx: ty::ctxt, as: [@pat], bs: [@pat]) -> bool {
|
2011-07-27 07:19:39 -05:00
|
|
|
let i = 0;
|
2011-08-15 23:54:52 -05:00
|
|
|
for a: @pat in as {
|
2011-08-19 17:16:48 -05:00
|
|
|
if !pattern_supersedes(tcx, a, bs[i]) { ret false; }
|
2011-07-25 06:45:09 -05:00
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
ret true;
|
|
|
|
}
|
2011-09-12 04:27:30 -05:00
|
|
|
fn field_patterns_supersede(tcx: ty::ctxt, fas: [field_pat],
|
|
|
|
fbs: [field_pat]) -> bool {
|
2011-08-15 15:33:12 -05:00
|
|
|
let wild = @{id: 0, node: pat_wild, span: dummy_sp()};
|
2011-08-15 23:54:52 -05:00
|
|
|
for fa: field_pat in fas {
|
2011-07-27 07:19:39 -05:00
|
|
|
let pb = wild;
|
2011-08-15 23:54:52 -05:00
|
|
|
for fb: field_pat in fbs {
|
2011-07-25 06:45:09 -05:00
|
|
|
if fa.ident == fb.ident { pb = fb.pat; }
|
|
|
|
}
|
|
|
|
if !pattern_supersedes(tcx, fa.pat, pb) { ret false; }
|
|
|
|
}
|
|
|
|
ret true;
|
|
|
|
}
|
|
|
|
|
|
|
|
alt a.node {
|
2012-01-14 18:05:07 -06:00
|
|
|
pat_ident(_, some(p)) { pattern_supersedes(tcx, p, b) }
|
2012-01-19 00:37:22 -06:00
|
|
|
pat_wild | pat_ident(_, none) { true }
|
2011-07-27 07:19:39 -05:00
|
|
|
pat_lit(la) {
|
2011-07-25 06:45:09 -05:00
|
|
|
alt b.node {
|
2011-12-08 04:56:16 -06:00
|
|
|
pat_lit(lb) { lit_expr_eq(la, lb) }
|
|
|
|
_ { false }
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
}
|
2012-01-25 07:34:31 -06:00
|
|
|
pat_enum(va, suba) {
|
2011-07-25 06:45:09 -05:00
|
|
|
alt b.node {
|
2012-01-25 07:34:31 -06:00
|
|
|
pat_enum(vb, subb) {
|
2011-12-08 04:56:16 -06:00
|
|
|
tcx.def_map.get(a.id) == tcx.def_map.get(b.id) &&
|
|
|
|
patterns_supersede(tcx, suba, subb)
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
2011-12-08 04:56:16 -06:00
|
|
|
_ { false }
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
}
|
2011-07-27 07:19:39 -05:00
|
|
|
pat_rec(suba, _) {
|
2011-07-25 06:45:09 -05:00
|
|
|
alt b.node {
|
2011-12-08 04:56:16 -06:00
|
|
|
pat_rec(subb, _) { field_patterns_supersede(tcx, suba, subb) }
|
|
|
|
_ { false }
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
}
|
2011-08-15 06:15:19 -05:00
|
|
|
pat_tup(suba) {
|
|
|
|
alt b.node {
|
2011-12-08 04:56:16 -06:00
|
|
|
pat_tup(subb) { patterns_supersede(tcx, suba, subb) }
|
|
|
|
_ { false }
|
2011-08-15 06:15:19 -05:00
|
|
|
}
|
|
|
|
}
|
2011-07-27 07:19:39 -05:00
|
|
|
pat_box(suba) {
|
2011-07-25 06:45:09 -05:00
|
|
|
alt b.node {
|
2011-12-08 04:56:16 -06:00
|
|
|
pat_box(subb) { pattern_supersedes(tcx, suba, subb) }
|
|
|
|
_ { pattern_supersedes(tcx, suba, b) }
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
}
|
2011-09-23 13:15:17 -05:00
|
|
|
pat_uniq(suba) {
|
|
|
|
alt b.node {
|
2011-12-08 04:56:16 -06:00
|
|
|
pat_uniq(subb) { pattern_supersedes(tcx, suba, subb) }
|
|
|
|
_ { pattern_supersedes(tcx, suba, b) }
|
2011-09-23 13:15:17 -05:00
|
|
|
}
|
|
|
|
}
|
2011-09-28 14:07:33 -05:00
|
|
|
pat_range(begina, enda) {
|
|
|
|
alt b.node {
|
|
|
|
pat_lit(lb) {
|
2011-12-08 04:56:16 -06:00
|
|
|
compare_lit_exprs(begina, lb) <= 0 &&
|
|
|
|
compare_lit_exprs(enda, lb) >= 0
|
2011-09-28 14:07:33 -05:00
|
|
|
}
|
|
|
|
pat_range(beginb, endb) {
|
2011-12-08 04:56:16 -06:00
|
|
|
compare_lit_exprs(begina, beginb) <= 0 &&
|
|
|
|
compare_lit_exprs(enda, endb) >= 0
|
2011-09-28 14:07:33 -05:00
|
|
|
}
|
2011-12-08 04:56:16 -06:00
|
|
|
_ { false }
|
2011-09-28 14:07:33 -05:00
|
|
|
}
|
|
|
|
}
|
2011-07-25 06:45:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-06 05:26:12 -05:00
|
|
|
fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) {
|
2011-08-01 08:26:48 -05:00
|
|
|
visit::visit_local(loc, s, v);
|
|
|
|
if is_refutable(tcx, loc.node.pat) {
|
|
|
|
tcx.sess.span_err(loc.node.pat.span,
|
2011-09-02 17:34:58 -05:00
|
|
|
"refutable pattern in local binding");
|
2011-08-01 08:26:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-12 04:27:30 -05:00
|
|
|
fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool {
|
2012-01-14 18:05:07 -06:00
|
|
|
alt normalize_pat(tcx, pat).node {
|
|
|
|
pat_box(sub) | pat_uniq(sub) | pat_ident(_, some(sub)) {
|
2011-12-08 04:56:16 -06:00
|
|
|
is_refutable(tcx, sub)
|
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
pat_wild | pat_ident(_, none) { false }
|
2011-12-08 04:56:16 -06:00
|
|
|
pat_lit(_) { true }
|
2011-08-01 08:26:48 -05:00
|
|
|
pat_rec(fields, _) {
|
2012-01-30 23:00:57 -06:00
|
|
|
for it: field_pat in fields {
|
|
|
|
if is_refutable(tcx, it.pat) { ret true; }
|
2011-08-01 08:26:48 -05:00
|
|
|
}
|
2011-12-08 04:56:16 -06:00
|
|
|
false
|
2011-08-01 08:26:48 -05:00
|
|
|
}
|
2011-08-15 06:15:19 -05:00
|
|
|
pat_tup(elts) {
|
2011-08-19 17:16:48 -05:00
|
|
|
for elt in elts { if is_refutable(tcx, elt) { ret true; } }
|
2011-12-08 04:56:16 -06:00
|
|
|
false
|
2011-08-15 06:15:19 -05:00
|
|
|
}
|
2012-01-25 07:34:31 -06:00
|
|
|
pat_enum(_, args) {
|
2011-08-01 08:26:48 -05:00
|
|
|
let vdef = variant_def_ids(tcx.def_map.get(pat.id));
|
2012-01-30 23:00:57 -06:00
|
|
|
if vec::len(*ty::enum_variants(tcx, vdef.enm)) != 1u { ret true; }
|
2011-08-19 17:16:48 -05:00
|
|
|
for p: @pat in args { if is_refutable(tcx, p) { ret true; } }
|
2011-12-08 04:56:16 -06:00
|
|
|
false
|
2011-08-01 08:26:48 -05:00
|
|
|
}
|
2012-01-30 23:00:57 -06:00
|
|
|
pat_range(_, _) { true }
|
2011-08-01 08:26:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-25 06:45:09 -05:00
|
|
|
// Local Variables:
|
|
|
|
// mode: rust
|
|
|
|
// fill-column: 78;
|
|
|
|
// indent-tabs-mode: nil
|
|
|
|
// c-basic-offset: 4
|
|
|
|
// buffer-file-coding-system: utf-8-unix
|
|
|
|
// End:
|