Implement pattern ranges for all numeric types.

This commit is contained in:
Josh Matthews 2011-09-21 03:00:32 -04:00 committed by Marijn Haverbeke
parent e6a84f252a
commit ce0f054f9d
14 changed files with 451 additions and 25 deletions

View file

@ -645,7 +645,7 @@ fn pattern_roots(tcx: ty::ctxt, mut: option::t<unsafe_ty>, pat: @ast::pat)
fn walk(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_wild. | ast::pat_lit(_) | ast::pat_range(_, _) {}
ast::pat_bind(nm) {
set += [{id: pat.id, name: nm, mut: mut, span: pat.span}];
}

View file

@ -62,12 +62,15 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
ret true;
}
alt a.node {
pat_wild. | pat_bind(_) { ret true; }
pat_lit(la) {
alt b.node {
pat_lit(lb) { ret util::common::lit_eq(la, lb); }
pat_range(beginb, endb) {
ret util::common::lit_type_eq(la, beginb) &&
util::common::lit_in_range(la, beginb, endb);
}
_ { ret false; }
}
}
@ -98,6 +101,19 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
_ { ret pattern_supersedes(tcx, suba, b); }
}
}
pat_range(begina, enda) {
alt b.node {
pat_lit(lb) {
ret util::common::lit_type_eq(lb, begina) &&
util::common::lit_in_range(lb, begina, enda);
}
pat_range(beginb, endb) {
ret util::common::lit_type_eq(begina, beginb) &&
util::common::lit_ranges_overlap(begina, enda, beginb, endb);
}
_ { ret false; }
}
}
}
}

View file

@ -1,4 +1,4 @@
import std::{str, vec, option};
import std::{str, vec, option, int};
import option::{some, none};
import std::map::hashmap;
@ -16,25 +16,42 @@ import util::common::lit_eq;
import trans_common::*;
// An option identifying a branch (either a literal or a tag variant)
// An option identifying a branch (either a literal, a tag variant or a range)
tag opt {
lit(@ast::lit);
var(/* variant id */uint, /* variant dids */{tg: def_id, var: def_id});
range(@ast::lit, @ast::lit);
}
fn opt_eq(a: opt, b: opt) -> bool {
alt a {
lit(la) {
ret alt b { lit(lb) { lit_eq(la, lb) } var(_, _) { false } };
ret alt b { lit(lb) { lit_eq(la, lb) } _ { false } };
}
var(ida, _) {
ret alt b { lit(_) { false } var(idb, _) { ida == idb } };
ret alt b { var(idb, _) { ida == idb } _ { false } };
}
range(la1, la2) {
ret alt b {
range(lb1, lb2) { lit_eq(la1, lb1) && lit_eq(la2, lb2) }
_ { false }
};
}
}
}
fn trans_opt(bcx: @block_ctxt, o: opt) -> result {
tag opt_result {
single_result(result);
range_result(result, result);
}
fn trans_opt(bcx: @block_ctxt, o: opt) -> opt_result {
alt o {
lit(l) { ret trans::trans_lit(bcx, *l); }
var(id, _) { ret rslt(bcx, C_int(id as int)); }
lit(l) { ret single_result(trans::trans_lit(bcx, *l)); }
var(id, _) { ret single_result(rslt(bcx, C_int(id as int))); }
range(l1, l2) {
let r1 = trans::trans_lit(bcx, *l1);
let r2 = trans::trans_lit(r1.bcx, *l2);
ret range_result(r1, r2);
}
}
}
@ -124,6 +141,9 @@ fn enter_opt(ccx: @crate_ctxt, m: match, opt: opt, col: uint, tag_size: uint,
ast::pat_lit(l) {
ret if opt_eq(lit(l), opt) { some([]) } else { none };
}
ast::pat_range(l1, l2) {
ret if opt_eq(range(l1, l2), opt) { some([]) } else { none };
}
_ { ret some(vec::init_elt(dummy, size)); }
}
}
@ -186,6 +206,9 @@ fn get_options(ccx: @crate_ctxt, m: match, col: uint) -> [opt] {
for br: match_branch in m {
alt br.pats[col].node {
ast::pat_lit(l) { add_to_set(found, lit(l)); }
ast::pat_range(l1, l2) {
add_to_set(found, range(l1, l2));
}
ast::pat_tag(_, _) {
add_to_set(found, variant_opt(ccx, br.pats[col].id));
}
@ -265,7 +288,9 @@ fn pick_col(m: match) -> uint {
let i = 0u;
for p: @ast::pat in br.pats {
alt p.node {
ast::pat_lit(_) | ast::pat_tag(_, _) { scores[i] += 1u; }
ast::pat_lit(_) | ast::pat_tag(_, _) | ast::pat_range(_, _) {
scores[i] += 1u;
}
_ { }
}
i += 1u;
@ -410,6 +435,16 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
_ { test_val = Load(bcx, val); switch }
};
}
range(_, _) {
test_val = Load(bcx, val);
kind = compare;
}
}
}
for o: opt in opts {
alt o {
range(_, _) { kind = compare; break; }
_ { }
}
}
let else_cx =
@ -428,22 +463,44 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
alt kind {
single. { Br(bcx, opt_cx.llbb); }
switch. {
let r = trans_opt(bcx, opt);
bcx = r.bcx;
llvm::LLVMAddCase(sw, r.val, opt_cx.llbb);
let res = trans_opt(bcx, opt);
alt res {
single_result(r) {
llvm::LLVMAddCase(sw, r.val, opt_cx.llbb);
bcx = r.bcx;
}
}
}
compare. {
let compare_cx = new_scope_block_ctxt(bcx, "compare_scope");
Br(bcx, compare_cx.llbb);
bcx = compare_cx;
let r = trans_opt(bcx, opt);
bcx = r.bcx;
let t = ty::node_id_to_type(ccx.tcx, pat_id);
let eq =
trans::trans_compare(bcx, ast::eq, test_val, t, r.val, t);
let cleanup_cx = trans::trans_block_cleanups(bcx, compare_cx);
bcx = new_sub_block_ctxt(bcx, "compare_next");
CondBr(cleanup_cx, eq.val, opt_cx.llbb, bcx.llbb);
let res = trans_opt(bcx, opt);
alt res {
single_result(r) {
bcx = r.bcx;
let eq =
trans::trans_compare(bcx, ast::eq, test_val, t, r.val, t);
/*let*/ bcx = eq.bcx; //XXX uncomment for assertion
let cleanup_cx = trans::trans_block_cleanups(bcx, compare_cx);
bcx = new_sub_block_ctxt(bcx, "compare_next");
CondBr(cleanup_cx, eq.val, opt_cx.llbb, bcx.llbb);
}
range_result(rbegin, rend) {
bcx = rend.bcx;
let ge = trans::trans_compare(bcx, ast::ge, test_val, t,
rbegin.val, t);
let le = trans::trans_compare(ge.bcx, ast::le, test_val, t,
rend.val, t);
let in_range = rslt(le.bcx, And(le.bcx, ge.val, le.val));
/*let*/ bcx = in_range.bcx; //XXX uncomment for assertion
let cleanup_cx =
trans::trans_block_cleanups(bcx, compare_cx);
bcx = new_sub_block_ctxt(bcx, "compare_next");
CondBr(cleanup_cx, in_range.val, opt_cx.llbb, bcx.llbb);
}
}
}
_ { }
}
@ -456,7 +513,7 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
unpacked = args.vals;
opt_cx = args.bcx;
}
lit(_) { }
lit(_) | range(_, _) { }
}
compile_submatch(opt_cx, enter_opt(ccx, m, opt, col, size, val),
unpacked + vals_left, f, exits);
@ -631,12 +688,13 @@ fn bind_irrefutable_pat(bcx: @block_ctxt, pat: @ast::pat, val: ValueRef,
[C_int(0), C_int(back::abi::box_rc_field_body)]);
bcx = bind_irrefutable_pat(bcx, inner, unboxed, table, true);
}
ast::pat_wild. | ast::pat_lit(_) { }
ast::pat_wild. | ast::pat_lit(_) | ast::pat_range(_, _) { }
}
ret bcx;
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4

View file

@ -1266,6 +1266,44 @@ fn check_lit(ccx: @crate_ctxt, lit: @ast::lit) -> ty::t {
}
}
fn lit_as_uint(l: @ast::lit) -> uint {
alt l.node {
ast::lit_uint(u) { u }
ast::lit_char(c) { c as uint }
}
}
fn lit_as_int(l: @ast::lit) -> int {
alt l.node {
ast::lit_int(i) | ast::lit_mach_int(_, i) { i }
}
}
fn lit_as_float(l: @ast::lit) -> str {
alt l.node {
ast::lit_float(f) | ast::lit_mach_float(_, f) { f }
}
}
fn valid_range_bounds(l1: @ast::lit, l2: @ast::lit) -> bool {
alt l1.node {
ast::lit_float(s1) | ast::lit_mach_float(_, s1) {
let s2 = lit_as_float(l2);
let f1 = util::common::str_to_float(s1);
let f2 = util::common::str_to_float(s2);
ret *util::common::min(f1, f2) == f1
}
ast::lit_uint(_) | ast::lit_char(_) {
let u1 = lit_as_uint(l1);
let u2 = lit_as_uint(l2);
ret *util::common::min(u1, u2) == u1
}
_ {
let i1 = lit_as_int(l1);
let i2 = lit_as_int(l2);
ret *util::common::min(i1, i2) == i1
}
}
}
// Pattern checking is top-down rather than bottom-up so that bindings get
// their types immediately.
fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat,
@ -1277,6 +1315,23 @@ fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat,
typ = demand::simple(fcx, pat.span, expected, typ);
write::ty_only_fixup(fcx, pat.id, typ);
}
ast::pat_range(begin, end) {
if !util::common::lit_is_numeric(begin) ||
!util::common::lit_is_numeric(end) {
fcx.ccx.tcx.sess.span_err(pat.span,
"non-numeric type used in range");
} else if !valid_range_bounds(begin, end) {
fcx.ccx.tcx.sess.span_err(begin.span,
"lower range bound must be less \
than upper");
}
let typ1 = check_lit(fcx.ccx, begin);
typ1 = demand::simple(fcx, pat.span, expected, typ1);
write::ty_only_fixup(fcx, pat.id, typ1);
let typ2 = check_lit(fcx.ccx, end);
typ2 = demand::simple(fcx, pat.span, typ1, typ2);
write::ty_only_fixup(fcx, pat.id, typ2);
}
ast::pat_bind(name) {
let vid = lookup_local(fcx, pat.span, pat.id);
let typ = ty::mk_var(fcx.ccx.tcx, vid);