1
Fork 0

Rewrite exhaustiveness checker

Issue #352
Closes #1720

The old checker would happily accept things like 'alt x { @some(a) { a } }'.
It now properly descends into patterns, checks exhaustiveness of booleans,
and complains when number/string patterns aren't exhaustive.
This commit is contained in:
Marijn Haverbeke 2012-02-15 09:40:42 +01:00
parent 4b63826050
commit 67cc89f38d
32 changed files with 193 additions and 125 deletions

View file

@ -218,7 +218,7 @@ fn lookup_def(cnum: ast::crate_num, data: @[u8], did_: ast::def_id) ->
let fam_ch = item_family(item); let fam_ch = item_family(item);
let did = {crate: cnum, node: did_.node}; let did = {crate: cnum, node: did_.node};
// We treat references to enums as references to types. // We treat references to enums as references to types.
alt fam_ch { alt check fam_ch {
'c' { ast::def_const(did) } 'c' { ast::def_const(did) }
'u' { ast::def_fn(did, ast::unsafe_fn) } 'u' { ast::def_fn(did, ast::unsafe_fn) }
'f' { ast::def_fn(did, ast::impure_fn) } 'f' { ast::def_fn(did, ast::impure_fn) }
@ -336,7 +336,7 @@ fn get_iface_methods(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
_ { tcx.sess.bug("get_iface_methods: id has non-function type"); _ { tcx.sess.bug("get_iface_methods: id has non-function type");
} }; } };
result += [{ident: name, tps: bounds, fty: fty, result += [{ident: name, tps: bounds, fty: fty,
purity: alt item_family(mth) { purity: alt check item_family(mth) {
'u' { ast::unsafe_fn } 'u' { ast::unsafe_fn }
'f' { ast::impure_fn } 'f' { ast::impure_fn }
'p' { ast::pure_fn } 'p' { ast::pure_fn }
@ -346,7 +346,7 @@ fn get_iface_methods(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
} }
fn family_has_type_params(fam_ch: char) -> bool { fn family_has_type_params(fam_ch: char) -> bool {
alt fam_ch { alt check fam_ch {
'c' | 'T' | 'm' | 'n' { false } 'c' | 'T' | 'm' | 'n' { false }
'f' | 'u' | 'p' | 'F' | 'U' | 'P' | 'y' | 't' | 'v' | 'i' | 'I' { true } 'f' | 'u' | 'p' | 'F' | 'U' | 'P' | 'y' | 't' | 'v' | 'i' | 'I' { true }
} }
@ -370,7 +370,7 @@ fn describe_def(items: ebml::doc, id: ast::def_id) -> str {
} }
fn item_family_to_str(fam: char) -> str { fn item_family_to_str(fam: char) -> str {
alt fam { alt check fam {
'c' { ret "const"; } 'c' { ret "const"; }
'f' { ret "fn"; } 'f' { ret "fn"; }
'u' { ret "unsafe fn"; } 'u' { ret "unsafe fn"; }

View file

@ -177,7 +177,7 @@ fn parse_proto(c: char) -> ast::proto {
} }
fn parse_ty(st: @pstate, conv: conv_did) -> ty::t { fn parse_ty(st: @pstate, conv: conv_did) -> ty::t {
alt next(st) { alt check next(st) {
'n' { ret ty::mk_nil(st.tcx); } 'n' { ret ty::mk_nil(st.tcx); }
'z' { ret ty::mk_bot(st.tcx); } 'z' { ret ty::mk_bot(st.tcx); }
'b' { ret ty::mk_bool(st.tcx); } 'b' { ret ty::mk_bool(st.tcx); }
@ -185,7 +185,7 @@ fn parse_ty(st: @pstate, conv: conv_did) -> ty::t {
'u' { ret ty::mk_uint(st.tcx); } 'u' { ret ty::mk_uint(st.tcx); }
'l' { ret ty::mk_float(st.tcx); } 'l' { ret ty::mk_float(st.tcx); }
'M' { 'M' {
alt next(st) { alt check next(st) {
'b' { ret ty::mk_mach_uint(st.tcx, ast::ty_u8); } 'b' { ret ty::mk_mach_uint(st.tcx, ast::ty_u8); }
'w' { ret ty::mk_mach_uint(st.tcx, ast::ty_u16); } 'w' { ret ty::mk_mach_uint(st.tcx, ast::ty_u16); }
'l' { ret ty::mk_mach_uint(st.tcx, ast::ty_u32); } 'l' { ret ty::mk_mach_uint(st.tcx, ast::ty_u32); }
@ -269,7 +269,7 @@ fn parse_ty(st: @pstate, conv: conv_did) -> ty::t {
'Y' { ret ty::mk_type(st.tcx); } 'Y' { ret ty::mk_type(st.tcx); }
'y' { ret ty::mk_send_type(st.tcx); } 'y' { ret ty::mk_send_type(st.tcx); }
'C' { 'C' {
let ck = alt next(st) { let ck = alt check next(st) {
'&' { ty::ck_block } '&' { ty::ck_block }
'@' { ty::ck_box } '@' { ty::ck_box }
'~' { ty::ck_uniq } '~' { ty::ck_uniq }
@ -355,7 +355,7 @@ fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::fn_ty {
assert (next(st) == '['); assert (next(st) == '[');
let inputs: [ty::arg] = []; let inputs: [ty::arg] = [];
while peek(st) != ']' { while peek(st) != ']' {
let mode = alt peek(st) { let mode = alt check peek(st) {
'&' { ast::by_mut_ref } '&' { ast::by_mut_ref }
'-' { ast::by_move } '-' { ast::by_move }
'+' { ast::by_copy } '+' { ast::by_copy }
@ -405,7 +405,7 @@ fn parse_bounds_data(data: @[u8], start: uint,
fn parse_bounds(st: @pstate, conv: conv_did) -> @[ty::param_bound] { fn parse_bounds(st: @pstate, conv: conv_did) -> @[ty::param_bound] {
let bounds = []; let bounds = [];
while true { while true {
bounds += [alt next(st) { bounds += [alt check next(st) {
'S' { ty::bound_send } 'S' { ty::bound_send }
'C' { ty::bound_copy } 'C' { ty::bound_copy }
'I' { ty::bound_iface(parse_ty(st, conv)) } 'I' { ty::bound_iface(parse_ty(st, conv)) }

View file

@ -27,7 +27,7 @@ fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) {
/* Check for exhaustiveness */ /* Check for exhaustiveness */
if mode == alt_exhaustive { if mode == alt_exhaustive {
let arms = vec::concat(vec::filter_map(arms, unguarded_pat)); let arms = vec::concat(vec::filter_map(arms, unguarded_pat));
check_exhaustive(tcx, ex.span, expr_ty(tcx, scrut), arms); check_exhaustive(tcx, ex.span, arms);
} }
} }
_ { } _ { }
@ -59,89 +59,136 @@ fn check_arms(tcx: ty::ctxt, arms: [arm]) {
} }
} }
fn raw_pat(p: @pat) -> @pat {
alt p.node {
pat_ident(_, some(s)) { raw_pat(s) }
_ { p }
}
}
// Precondition: patterns have been normalized // Precondition: patterns have been normalized
// (not checked statically yet) // (not checked statically yet)
fn check_exhaustive(tcx: ty::ctxt, sp:span, scrut_ty:ty::t, pats:[@pat]) { fn check_exhaustive(tcx: ty::ctxt, sp: span, pats: [@pat]) {
let represented : [def_id] = []; if pats.len() == 0u {
/* Determine the type of the scrutinee */ tcx.sess.span_err(sp, "non-exhaustive patterns");
/* If it's not an enum, exit (bailing out on checking non-enum alts ret;
for now) */ }
/* Otherwise, get the list of variants and make sure each one is // If there a non-refutable pattern in the set, we're okay.
represented. Then recurse on the columns. */ for pat in pats { if !is_refutable(tcx, pat) { ret; } }
let ty_def_id = alt ty::get(scrut_ty).struct { alt ty::get(ty::node_id_to_type(tcx, pats[0].id)).struct {
ty_enum(id, _) { id } ty::ty_enum(id, _) {
_ { ret; } }; check_exhaustive_enum(tcx, id, sp, pats);
}
ty::ty_box(_) {
check_exhaustive(tcx, sp, vec::filter_map(pats, {|p|
alt raw_pat(p).node { pat_box(sub) { some(sub) } _ { none } }
}));
}
ty::ty_uniq(_) {
check_exhaustive(tcx, sp, vec::filter_map(pats, {|p|
alt raw_pat(p).node { pat_uniq(sub) { some(sub) } _ { none } }
}));
}
ty::ty_tup(ts) {
let cols = vec::init_elt_mut(ts.len(), []);
for p in pats {
alt raw_pat(p).node {
pat_tup(sub) {
vec::iteri(sub) {|i, sp| cols[i] += [sp];}
}
_ {}
}
}
vec::iter(cols) {|col| check_exhaustive(tcx, sp, col); }
}
ty::ty_rec(fs) {
let cols = vec::init_elt(fs.len(), {mutable wild: false,
mutable pats: []});
for p in pats {
alt raw_pat(p).node {
pat_rec(sub, _) {
vec::iteri(fs) {|i, field|
alt vec::find(sub, {|pf| pf.ident == field.ident }) {
some(pf) { cols[i].pats += [pf.pat]; }
none { cols[i].wild = true; }
}
}
}
_ {}
}
}
vec::iter(cols) {|col|
if !col.wild { check_exhaustive(tcx, sp, copy col.pats); }
}
}
ty::ty_bool {
let saw_true = false, saw_false = false;
for p in pats {
alt raw_pat(p).node {
pat_lit(@{node: expr_lit(@{node: lit_bool(b), _}), _}) {
if b { saw_true = true; }
else { saw_false = true; }
}
_ {}
}
}
if !saw_true { tcx.sess.span_err(
sp, "non-exhaustive bool patterns: true not covered"); }
if !saw_false { tcx.sess.span_err(
sp, "non-exhaustive bool patterns: false not covered"); }
}
ty::ty_nil {
let seen = vec::any(pats, {|p|
alt raw_pat(p).node {
pat_lit(@{node: expr_lit(@{node: lit_nil, _}), _}) { true }
_ { false }
}
});
if !seen { tcx.sess.span_err(sp, "non-exhaustive patterns"); }
}
// Literal patterns are always considered non-exhaustive
_ {
tcx.sess.span_err(sp, "non-exhaustive literal patterns");
}
}
}
fn check_exhaustive_enum(tcx: ty::ctxt, enum_id: def_id, sp: span,
pats: [@pat]) {
let variants = enum_variants(tcx, enum_id);
let columns_by_variant = vec::map(*variants, {|v|
{mutable seen: false,
cols: vec::init_elt_mut(v.args.len(), [])}
});
let variants = *enum_variants(tcx, ty_def_id);
for pat in pats { for pat in pats {
if !is_refutable(tcx, pat) { let pat = raw_pat(pat);
/* automatically makes this alt complete */ ret; alt tcx.def_map.get(pat.id) {
} def_variant(_, id) {
let variant_idx =
option::get(vec::position(*variants, {|v| v.id == id}));
columns_by_variant[variant_idx].seen = true;
alt pat.node { alt pat.node {
// want the def_id for the constructor pat_enum(_, args) {
pat_enum(id,_) { vec::iteri(args) {|i, p|
alt tcx.def_map.find(pat.id) { columns_by_variant[variant_idx].cols[i] += [p];
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 {
!vec::contains(v, vinfo.id)
}
// 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; vec::iteri(columns_by_variant) {|i, cv|
for it in rows[0] { if !cv.seen {
let column = [it]; tcx.sess.span_err(sp, "non-exhaustive patterns: variant `" +
// Annoying -- see comment in variants[i].name + "` not covered");
// tstate::states::find_pre_post_state_loop } else {
check vec::is_not_empty(rows); vec::iter(cv.cols) {|col| check_exhaustive(tcx, sp, col); }
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; }
} }
} }

View file

@ -40,7 +40,7 @@ fn time(do_it: bool, what: str, thunk: fn()) {
fn merge_opts(attrs: [ast::attribute], cmd_opts: [(option, bool)]) -> fn merge_opts(attrs: [ast::attribute], cmd_opts: [(option, bool)]) ->
[(option, bool)] { [(option, bool)] {
fn str_to_option(name: str) -> (option, bool) { fn str_to_option(name: str) -> (option, bool) {
ret alt name { ret alt check name {
"ctypes" { (ctypes, true) } "ctypes" { (ctypes, true) }
"no_ctypes" { (ctypes, false) } "no_ctypes" { (ctypes, false) }
} }

View file

@ -1023,7 +1023,7 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
} }
ast::expr_be(result) { word_nbsp(s, "be"); print_expr(s, result); } ast::expr_be(result) { word_nbsp(s, "be"); print_expr(s, result); }
ast::expr_log(lvl, lexp, expr) { ast::expr_log(lvl, lexp, expr) {
alt lvl { alt check lvl {
1 { word_nbsp(s, "log"); print_expr(s, expr); } 1 { word_nbsp(s, "log"); print_expr(s, expr); }
0 { word_nbsp(s, "log_err"); print_expr(s, expr); } 0 { word_nbsp(s, "log_err"); print_expr(s, expr); }
2 { 2 {

View file

@ -68,7 +68,7 @@ fn run(lib_path: str, prog: str, args: [str],
let count = 2; let count = 2;
while count > 0 { while count > 0 {
let stream = comm::recv(p); let stream = comm::recv(p);
alt stream { alt check stream {
(1, s) { (1, s) {
outs = s; outs = s;
} }

View file

@ -60,9 +60,10 @@ pure fn is_false(v: t) -> bool { !v }
brief = "Parse logic value from `s`" brief = "Parse logic value from `s`"
)] )]
pure fn from_str(s: str) -> t { pure fn from_str(s: str) -> t {
alt s { alt check s {
"true" { true } "true" { true }
"false" { false } "false" { false }
_ { fail "'" + s + "' is not a valid boolean string"; }
} }
} }

View file

@ -2140,7 +2140,7 @@ mod tests {
fn test_chars_iter() { fn test_chars_iter() {
let i = 0; let i = 0;
chars_iter("x\u03c0y") {|ch| chars_iter("x\u03c0y") {|ch|
alt i { alt check i {
0 { assert ch == 'x'; } 0 { assert ch == 'x'; }
1 { assert ch == '\u03c0'; } 1 { assert ch == '\u03c0'; }
2 { assert ch == 'y'; } 2 { assert ch == 'y'; }
@ -2156,7 +2156,7 @@ mod tests {
let i = 0; let i = 0;
bytes_iter("xyz") {|bb| bytes_iter("xyz") {|bb|
alt i { alt check i {
0 { assert bb == 'x' as u8; } 0 { assert bb == 'x' as u8; }
1 { assert bb == 'y' as u8; } 1 { assert bb == 'y' as u8; }
2 { assert bb == 'z' as u8; } 2 { assert bb == 'z' as u8; }

View file

@ -166,7 +166,7 @@ Function: from_str
Parse logic value from `s` Parse logic value from `s`
*/ */
pure fn from_str(s: str) -> t { pure fn from_str(s: str) -> t {
alt s { alt check s {
"none" { none } "none" { none }
"false" { four::false } "false" { four::false }
"true" { four::true } "true" { four::true }
@ -181,7 +181,7 @@ Convert `v` into a string
*/ */
pure fn to_str(v: t) -> str { pure fn to_str(v: t) -> str {
// FIXME replace with consts as soon as that works // FIXME replace with consts as soon as that works
alt v { alt check v {
0u8 { "none" } 0u8 { "none" }
1u8 { "true" } 1u8 { "true" }
2u8 { "false" } 2u8 { "false" }
@ -265,7 +265,7 @@ mod tests {
} }
fn to_tup(v: four::t) -> (bool, bool) { fn to_tup(v: four::t) -> (bool, bool) {
alt v { alt check v {
0u8 { (false, false) } 0u8 { (false, false) }
1u8 { (false, true) } 1u8 { (false, true) }
2u8 { (true, false) } 2u8 { (true, false) }

View file

@ -241,7 +241,7 @@ fn test_option_int() {
fn deserialize_0<S: deserializer>(s: S) -> option<int> { fn deserialize_0<S: deserializer>(s: S) -> option<int> {
s.read_enum("option") {|| s.read_enum("option") {||
s.read_enum_variant {|i| s.read_enum_variant {|i|
alt i { alt check i {
0u { none } 0u { none }
1u { 1u {
let v0 = s.read_enum_variant_arg(0u) {|| let v0 = s.read_enum_variant_arg(0u) {||

View file

@ -52,8 +52,12 @@ fn insert<K: copy, V: copy>(m: treemap<K, V>, k: K, v: V) {
// We have to name left and right individually, because // We have to name left and right individually, because
// otherwise the alias checker complains. // otherwise the alias checker complains.
if k < kk { if k < kk {
alt m { @node(_, _, left, _) { insert(left, k, v); } } alt check m { @node(_, _, left, _) { insert(left, k, v); } }
} else { alt m { @node(_, _, _, right) { insert(right, k, v); } } } } else {
alt check m {
@node(_, _, _, right) { insert(right, k, v); }
}
}
} }
} }
} }

View file

@ -137,7 +137,7 @@ Function: from_str
Parse logic value from `s` Parse logic value from `s`
*/ */
pure fn from_str(s: str) -> t { pure fn from_str(s: str) -> t {
alt s { alt check s {
"unknown" { unknown } "unknown" { unknown }
"true" { tri::true } "true" { tri::true }
"false" { tri::false } "false" { tri::false }
@ -151,7 +151,7 @@ Convert `v` into a string
*/ */
pure fn to_str(v: t) -> str { pure fn to_str(v: t) -> str {
// FIXME replace with consts as soon as that works // FIXME replace with consts as soon as that works
alt v { alt check v {
0u8 { "unknown" } 0u8 { "unknown" }
1u8 { "true" } 1u8 { "true" }
2u8 { "false" } 2u8 { "false" }

View file

@ -3,7 +3,7 @@
fn f() -> int { fn f() -> int {
// Make sure typestate doesn't interpret this alt expression // Make sure typestate doesn't interpret this alt expression
// as the function result // as the function result
alt true { true { } }; alt check true { true { } };
} }
fn main() { } fn main() { }

View file

@ -1,5 +1,5 @@
// -*- rust -*- // -*- rust -*-
// error-pattern: Non-exhaustive pattern // error-pattern: non-exhaustive patterns
enum t { a(u), b } enum t { a(u), b }
enum u { c, d } enum u { c, d }

View file

@ -1,5 +1,15 @@
// -*- rust -*-
// error-pattern: Non-exhaustive pattern
enum t { a, b, } enum t { a, b, }
fn main() { let x = a; alt x { b { } } } fn main() {
let x = a;
alt x { b { } } //! ERROR non-exhaustive patterns
alt true { //! ERROR non-exhaustive bool patterns
true {}
}
alt @some(10) { //! ERROR non-exhaustive patterns
@none {}
}
alt (2, 3, 4) { //! ERROR non-exhaustive literal patterns
(_, _, 4) {}
}
}

View file

@ -8,7 +8,7 @@ fn test2() -> int { let val = @0; { } *val }
fn test3() { fn test3() {
let regs = @{mutable eax: 0}; let regs = @{mutable eax: 0};
alt true { true { } } alt check true { true { } }
(*regs).eax = 1; (*regs).eax = 1;
} }
@ -20,13 +20,13 @@ fn test6() -> bool { { } (true || false) && true }
fn test7() -> uint { fn test7() -> uint {
let regs = @0; let regs = @0;
alt true { true { } } alt check true { true { } }
(*regs < 2) as uint (*regs < 2) as uint
} }
fn test8() -> int { fn test8() -> int {
let val = @0; let val = @0;
alt true { alt check true {
true { } true { }
} }
if *val < 1 { if *val < 1 {
@ -36,11 +36,11 @@ fn test8() -> int {
} }
} }
fn test9() { let regs = @mutable 0; alt true { true { } } *regs += 1; } fn test9() { let regs = @mutable 0; alt check true { true { } } *regs += 1; }
fn test10() -> int { fn test10() -> int {
let regs = @mutable [0]; let regs = @mutable [0];
alt true { true { } } alt check true { true { } }
(*regs)[0] (*regs)[0]
} }

View file

@ -10,8 +10,8 @@ fn if_semi() -> int { if true { f() } else { f() }; -1 }
fn if_nosemi() -> int { (if true { 0 } else { 0 }) - 1 } fn if_nosemi() -> int { (if true { 0 } else { 0 }) - 1 }
fn alt_semi() -> int { alt true { true { f() } }; -1 } fn alt_semi() -> int { alt check true { true { f() } }; -1 }
fn alt_no_semi() -> int { (alt true { true { 0 } }) - 1 } fn alt_no_semi() -> int { (alt check true { true { 0 } }) - 1 }
fn stmt() { { f() }; -1; } fn stmt() { { f() }; -1; }

View file

@ -4,7 +4,7 @@ fn test_box() {
@0; @0;
} }
fn test_str() { fn test_str() {
let res = alt false { true { "happy" } }; let res = alt check false { true { "happy" } };
assert res == "happy"; assert res == "happy";
} }
fn main() { fn main() {

View file

@ -1,3 +1,3 @@
// n.b. This was only ever failing with optimization disabled. // n.b. This was only ever failing with optimization disabled.
fn a() -> int { alt ret 1 { 2 { 3 } } } fn a() -> int { alt check ret 1 { 2 { 3 } } }
fn main() { a(); } fn main() { a(); }

View file

@ -1,7 +1,7 @@
fn altlit(f: int) -> int { fn altlit(f: int) -> int {
alt f { alt check f {
10 { #debug("case 10"); ret 20; } 10 { #debug("case 10"); ret 20; }
11 { #debug("case 11"); ret 22; } 11 { #debug("case 11"); ret 22; }
} }

View file

@ -7,7 +7,7 @@ fn main() {
6u to 7u { fail "shouldn't match range"; } 6u to 7u { fail "shouldn't match range"; }
_ {} _ {}
} }
alt 5u { alt check 5u {
1u { fail "should match non-first range"; } 1u { fail "should match non-first range"; }
2u to 6u {} 2u to 6u {}
} }

View file

@ -1,7 +1,7 @@
// Issue #53 // Issue #53
fn main() { fn main() {
alt "test" { "not-test" { fail; } "test" { } _ { fail; } } alt check "test" { "not-test" { fail; } "test" { } _ { fail; } }
enum t { tag1(str), tag2, } enum t { tag1(str), tag2, }
@ -13,9 +13,9 @@ fn main() {
_ { fail; } _ { fail; }
} }
let x = alt "a" { "a" { 1 } "b" { 2 } }; let x = alt check "a" { "a" { 1 } "b" { 2 } };
assert (x == 1); assert (x == 1);
alt "a" { "a" { } "b" { } } alt check "a" { "a" { } "b" { } }
} }

View file

@ -1,6 +1,6 @@
// Check that issue #954 stays fixed // Check that issue #954 stays fixed
fn main() { fn main() {
alt -1 { -1 {} } alt check -1 { -1 {} }
assert 1-1 == 0; assert 1-1 == 0;
} }

View file

@ -4,10 +4,13 @@
// -*- rust -*- // -*- rust -*-
// Tests for alt as expressions resulting in boxed types // Tests for alt as expressions resulting in boxed types
fn test_box() { let res = alt true { true { @100 } }; assert (*res == 100); } fn test_box() {
let res = alt check true { true { @100 } };
assert (*res == 100);
}
fn test_str() { fn test_str() {
let res = alt true { true { "happy" } }; let res = alt check true { true { "happy" } };
assert (res == "happy"); assert (res == "happy");
} }

View file

@ -5,7 +5,7 @@
type compare<T> = fn@(@T, @T) -> bool; type compare<T> = fn@(@T, @T) -> bool;
fn test_generic<T>(expected: @T, eq: compare<T>) { fn test_generic<T>(expected: @T, eq: compare<T>) {
let actual: @T = alt true { true { expected } }; let actual: @T = alt check true { true { expected } };
assert (eq(expected, actual)); assert (eq(expected, actual));
} }

View file

@ -5,7 +5,7 @@
type compare<T> = fn@(T, T) -> bool; type compare<T> = fn@(T, T) -> bool;
fn test_generic<T: copy>(expected: T, eq: compare<T>) { fn test_generic<T: copy>(expected: T, eq: compare<T>) {
let actual: T = alt true { true { expected } }; let actual: T = alt check true { true { expected } };
assert (eq(expected, actual)); assert (eq(expected, actual));
} }

View file

@ -4,7 +4,7 @@
type compare<T> = fn@(~T, ~T) -> bool; type compare<T> = fn@(~T, ~T) -> bool;
fn test_generic<T: copy>(expected: ~T, eq: compare<T>) { fn test_generic<T: copy>(expected: ~T, eq: compare<T>) {
let actual: ~T = alt true { true { expected } }; let actual: ~T = alt check true { true { expected } };
assert (eq(expected, actual)); assert (eq(expected, actual));
} }

View file

@ -5,7 +5,7 @@
type compare<T> = fn@(T, T) -> bool; type compare<T> = fn@(T, T) -> bool;
fn test_generic<T: copy>(expected: T, eq: compare<T>) { fn test_generic<T: copy>(expected: T, eq: compare<T>) {
let actual: T = alt true { true { expected } }; let actual: T = alt check true { true { expected } };
assert (eq(expected, actual)); assert (eq(expected, actual));
} }

View file

@ -5,7 +5,7 @@
type compare<T> = fn@(T, T) -> bool; type compare<T> = fn@(T, T) -> bool;
fn test_generic<T: copy>(expected: T, eq: compare<T>) { fn test_generic<T: copy>(expected: T, eq: compare<T>) {
let actual: T = alt true { true { expected } }; let actual: T = alt check true { true { expected } };
assert (eq(expected, actual)); assert (eq(expected, actual));
} }

View file

@ -5,7 +5,7 @@
// Tests for alt as expressions resulting in structural types // Tests for alt as expressions resulting in structural types
fn test_rec() { fn test_rec() {
let rs = alt true { true { {i: 100} } }; let rs = alt check true { true { {i: 100} } };
assert (rs == {i: 100}); assert (rs == {i: 100});
} }

View file

@ -4,6 +4,9 @@
// -*- rust -*- // -*- rust -*-
// Tests for alt as expressions resulting in boxed types // Tests for alt as expressions resulting in boxed types
fn test_box() { let res = alt true { true { ~100 } }; assert (*res == 100); } fn test_box() {
let res = alt check true { true { ~100 } };
assert (*res == 100);
}
fn main() { test_box(); } fn main() { test_box(); }

View file

@ -30,7 +30,7 @@ fn log_cont() { do { log(error, cont); } while false }
fn ret_ret() -> int { ret (ret 2) + 3; } fn ret_ret() -> int { ret (ret 2) + 3; }
fn ret_guard() { fn ret_guard() {
alt 2 { alt check 2 {
x if (ret) { x; } x if (ret) { x; }
} }
} }