Allow tail expressions even in no_value blocks. Type checker
will guarantee they have unit type.
This commit is contained in:
parent
43a9d50a74
commit
72a3667eb3
16 changed files with 97 additions and 55 deletions
|
@ -1741,7 +1741,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
|
||||||
let element_ty = demand::simple(fcx, local.span, element_ty,
|
let element_ty = demand::simple(fcx, local.span, element_ty,
|
||||||
ty::mk_var(fcx.ccx.tcx, locid));
|
ty::mk_var(fcx.ccx.tcx, locid));
|
||||||
let bot = check_decl_local(fcx, local);
|
let bot = check_decl_local(fcx, local);
|
||||||
check_block(fcx, body);
|
check_block_no_value(fcx, body);
|
||||||
// Unify type of decl with element type of the seq
|
// Unify type of decl with element type of the seq
|
||||||
demand::simple(fcx, local.span,
|
demand::simple(fcx, local.span,
|
||||||
ty::node_id_to_type(fcx.ccx.tcx, local.node.id),
|
ty::node_id_to_type(fcx.ccx.tcx, local.node.id),
|
||||||
|
@ -1756,22 +1756,27 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
|
||||||
fn check_then_else(fcx: @fn_ctxt, thn: ast::blk,
|
fn check_then_else(fcx: @fn_ctxt, thn: ast::blk,
|
||||||
elsopt: option::t<@ast::expr>, id: ast::node_id,
|
elsopt: option::t<@ast::expr>, id: ast::node_id,
|
||||||
_sp: span) -> bool {
|
_sp: span) -> bool {
|
||||||
let then_bot = check_block(fcx, thn);
|
let (if_t, if_bot) =
|
||||||
let els_bot = false;
|
|
||||||
let if_t =
|
|
||||||
alt elsopt {
|
alt elsopt {
|
||||||
some(els) {
|
some(els) {
|
||||||
|
let thn_bot = check_block(fcx, thn);
|
||||||
let thn_t = block_ty(fcx.ccx.tcx, thn);
|
let thn_t = block_ty(fcx.ccx.tcx, thn);
|
||||||
els_bot = check_expr_with(fcx, els, thn_t);
|
let els_bot = check_expr_with(fcx, els, thn_t);
|
||||||
let elsopt_t = expr_ty(fcx.ccx.tcx, els);
|
let els_t = expr_ty(fcx.ccx.tcx, els);
|
||||||
if !ty::type_is_bot(fcx.ccx.tcx, elsopt_t) {
|
let if_t = if !ty::type_is_bot(fcx.ccx.tcx, els_t) {
|
||||||
elsopt_t
|
els_t
|
||||||
} else { thn_t }
|
} else {
|
||||||
|
thn_t
|
||||||
|
};
|
||||||
|
(if_t, thn_bot & els_bot)
|
||||||
|
}
|
||||||
|
none. {
|
||||||
|
check_block_no_value(fcx, thn);
|
||||||
|
(ty::mk_nil(fcx.ccx.tcx), false)
|
||||||
}
|
}
|
||||||
none. { ty::mk_nil(fcx.ccx.tcx) }
|
|
||||||
};
|
};
|
||||||
write::ty_only_fixup(fcx, id, if_t);
|
write::ty_only_fixup(fcx, id, if_t);
|
||||||
ret then_bot & els_bot;
|
ret if_bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks the compatibility
|
// Checks the compatibility
|
||||||
|
@ -1993,12 +1998,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
|
||||||
}
|
}
|
||||||
ast::expr_while(cond, body) {
|
ast::expr_while(cond, body) {
|
||||||
bot = check_expr_with(fcx, cond, ty::mk_bool(tcx));
|
bot = check_expr_with(fcx, cond, ty::mk_bool(tcx));
|
||||||
check_block(fcx, body);
|
check_block_no_value(fcx, body);
|
||||||
write::ty_only_fixup(fcx, id, ty::mk_nil(tcx));
|
write::ty_only_fixup(fcx, id, ty::mk_nil(tcx));
|
||||||
}
|
}
|
||||||
ast::expr_do_while(body, cond) {
|
ast::expr_do_while(body, cond) {
|
||||||
bot = check_expr_with(fcx, cond, ty::mk_bool(tcx)) |
|
bot = check_expr_with(fcx, cond, ty::mk_bool(tcx)) |
|
||||||
check_block(fcx, body);
|
check_block_no_value(fcx, body);
|
||||||
write::ty_only_fixup(fcx, id, block_ty(tcx, body));
|
write::ty_only_fixup(fcx, id, block_ty(tcx, body));
|
||||||
}
|
}
|
||||||
ast::expr_alt(expr, arms) {
|
ast::expr_alt(expr, arms) {
|
||||||
|
@ -2490,6 +2495,16 @@ fn check_stmt(fcx: @fn_ctxt, stmt: @ast::stmt) -> bool {
|
||||||
ret bot;
|
ret bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_block_no_value(fcx: @fn_ctxt, blk: ast::blk) -> bool {
|
||||||
|
let bot = check_block(fcx, blk);
|
||||||
|
if !bot {
|
||||||
|
let blkty = ty::node_id_to_monotype(fcx.ccx.tcx, blk.node.id);
|
||||||
|
let nilty = ty::mk_nil(fcx.ccx.tcx);
|
||||||
|
demand::simple(fcx, blk.span, nilty, blkty);
|
||||||
|
}
|
||||||
|
ret bot;
|
||||||
|
}
|
||||||
|
|
||||||
fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool {
|
fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool {
|
||||||
let fcx = alt blk.node.rules {
|
let fcx = alt blk.node.rules {
|
||||||
ast::unchecked_blk. { @{purity: ast::impure_fn with *fcx0} }
|
ast::unchecked_blk. { @{purity: ast::impure_fn with *fcx0} }
|
||||||
|
|
|
@ -56,10 +56,10 @@ fn parse_companion_mod(cx: ctx, prefix: str, suffix: option::t<str>)
|
||||||
-> ([@ast::view_item], [@ast::item], [ast::attribute]) {
|
-> ([@ast::view_item], [@ast::item], [ast::attribute]) {
|
||||||
|
|
||||||
fn companion_file(prefix: str, suffix: option::t<str>) -> str {
|
fn companion_file(prefix: str, suffix: option::t<str>) -> str {
|
||||||
alt suffix {
|
ret alt suffix {
|
||||||
option::some(s) { fs::connect(prefix, s) }
|
option::some(s) { fs::connect(prefix, s) }
|
||||||
option::none. { prefix }
|
option::none. { prefix }
|
||||||
} + ".rs"
|
} + ".rs";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_exists(path: str) -> bool {
|
fn file_exists(path: str) -> bool {
|
||||||
|
|
|
@ -1232,11 +1232,6 @@ fn parse_if_expr_1(p: parser) ->
|
||||||
let elexpr = parse_else_expr(p);
|
let elexpr = parse_else_expr(p);
|
||||||
els = some(elexpr);
|
els = some(elexpr);
|
||||||
hi = elexpr.span.hi;
|
hi = elexpr.span.hi;
|
||||||
} else if !option::is_none(thn.node.expr) {
|
|
||||||
let sp = option::get(thn.node.expr).span;
|
|
||||||
p.span_fatal(sp, "`if` without `else` can not produce a result");
|
|
||||||
//TODO: If a suggestion mechanism appears, suggest that the
|
|
||||||
//user may have forgotten a ';'
|
|
||||||
}
|
}
|
||||||
ret {cond: cond, then: thn, els: els, lo: lo, hi: hi};
|
ret {cond: cond, then: thn, els: els, lo: lo, hi: hi};
|
||||||
}
|
}
|
||||||
|
@ -1596,32 +1591,29 @@ fn parse_stmt(p: parser) -> @ast::stmt {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_is_complete(p: parser, e: @ast::expr) -> bool {
|
fn expr_is_complete(p: parser, e: @ast::expr) -> bool {
|
||||||
|
log(debug, ("expr_is_complete", p.get_restriction(),
|
||||||
|
print::pprust::expr_to_str(e),
|
||||||
|
expr_requires_semi_to_be_stmt(e)));
|
||||||
ret p.get_restriction() == RESTRICT_STMT_EXPR &&
|
ret p.get_restriction() == RESTRICT_STMT_EXPR &&
|
||||||
!expr_requires_semi_to_be_stmt(e);
|
!expr_requires_semi_to_be_stmt(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
|
fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
|
||||||
alt e.node {
|
alt e.node {
|
||||||
ast::expr_if(_, th, els) | ast::expr_if_check(_, th, els) {
|
ast::expr_if(_, _, _) | ast::expr_if_check(_, _, _)
|
||||||
if option::is_none(els) { false }
|
| ast::expr_alt(_, _) | ast::expr_block(_)
|
||||||
else { !option::is_none(th.node.expr) ||
|
| ast::expr_do_while(_, _) | ast::expr_while(_, _)
|
||||||
expr_requires_semi_to_be_stmt(option::get(els)) }
|
| ast::expr_for(_, _, _)
|
||||||
|
| ast::expr_call(_, _, true) {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
ast::expr_alt(_, arms) {
|
|
||||||
vec::any(arms, {|arm| !option::is_none(arm.body.node.expr)})
|
|
||||||
}
|
|
||||||
ast::expr_block(blk) | ast::expr_while(_, blk) |
|
|
||||||
ast::expr_for(_, _, blk) | ast::expr_do_while(blk, _) {
|
|
||||||
!option::is_none(blk.node.expr)
|
|
||||||
}
|
|
||||||
ast::expr_call(_, _, true) { false }
|
|
||||||
_ { true }
|
_ { true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stmt_to_expr(stmt: @ast::stmt) -> option::t<@ast::expr> {
|
fn stmt_to_expr(stmt: @ast::stmt) -> option::t<@ast::expr> {
|
||||||
alt stmt.node {
|
alt stmt.node {
|
||||||
ast::stmt_expr(e, _) when expr_requires_semi_to_be_stmt(e) { some(e) }
|
ast::stmt_expr(e, _) { some(e) }
|
||||||
_ { none }
|
_ { none }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1655,14 +1647,10 @@ fn parse_block(p: parser) -> ast::blk {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block_no_value(p: parser) -> ast::blk {
|
fn parse_block_no_value(p: parser) -> ast::blk {
|
||||||
let blk = parse_block(p);
|
// We parse blocks that cannot have a value the same as any other block;
|
||||||
if !option::is_none(blk.node.expr) {
|
// the type checker will make sure that the tail expression (if any) has
|
||||||
let sp = option::get(blk.node.expr).span;
|
// unit type.
|
||||||
p.span_fatal(sp, "this block must not have a result");
|
ret parse_block(p);
|
||||||
//TODO: If a suggestion mechanism appears, suggest that the
|
|
||||||
//user may have forgotten a ';'
|
|
||||||
}
|
|
||||||
ret blk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Precondition: already parsed the '{' or '#{'
|
// Precondition: already parsed the '{' or '#{'
|
||||||
|
|
|
@ -558,11 +558,39 @@ fn print_attribute(s: ps, attr: ast::attribute) {
|
||||||
word(s.s, "]");
|
word(s.s, "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An expression that begins with a dual-form statement/expression like `{
|
||||||
|
// ... }-10` would be parsed as `{ ... };-10` unless parentheses are used (ie,
|
||||||
|
// `({...}-10)`). These parentheses are not, however, preserved by the
|
||||||
|
// parser. This function specifies whether parentheses must be inserted.
|
||||||
|
fn stmt_expr_requires_parens(ex: @ast::expr) -> bool {
|
||||||
|
fn helper(ex: @ast::expr, inner: bool) -> bool {
|
||||||
|
alt ex.node {
|
||||||
|
ast::expr_call(subex, _, _) | ast::expr_binary(_, subex, _) {
|
||||||
|
be helper(subex, true);
|
||||||
|
}
|
||||||
|
_ when !inner { ret false; }
|
||||||
|
_ { ret !parse::parser::expr_requires_semi_to_be_stmt(ex); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret helper(ex, false);
|
||||||
|
}
|
||||||
|
|
||||||
fn print_stmt(s: ps, st: ast::stmt) {
|
fn print_stmt(s: ps, st: ast::stmt) {
|
||||||
maybe_print_comment(s, st.span.lo);
|
maybe_print_comment(s, st.span.lo);
|
||||||
alt st.node {
|
alt st.node {
|
||||||
ast::stmt_decl(decl, _) { print_decl(s, decl); }
|
ast::stmt_decl(decl, _) {
|
||||||
ast::stmt_expr(expr, _) { space_if_not_bol(s); print_expr(s, expr); }
|
print_decl(s, decl);
|
||||||
|
}
|
||||||
|
ast::stmt_expr(expr, _) {
|
||||||
|
space_if_not_bol(s);
|
||||||
|
if stmt_expr_requires_parens(expr) {
|
||||||
|
popen(s);
|
||||||
|
print_expr(s, expr);
|
||||||
|
pclose(s);
|
||||||
|
} else {
|
||||||
|
print_expr(s, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if parse::parser::stmt_ends_with_semi(st) { word(s.s, ";"); }
|
if parse::parser::stmt_ends_with_semi(st) { word(s.s, ";"); }
|
||||||
maybe_print_trailing_comment(s, st.span, none::<uint>);
|
maybe_print_trailing_comment(s, st.span, none::<uint>);
|
||||||
|
|
|
@ -443,7 +443,7 @@ fn iter_chars(rope: rope, it: block(char)) {
|
||||||
loop_chars(rope) {|x|
|
loop_chars(rope) {|x|
|
||||||
it(x);
|
it(x);
|
||||||
ret true
|
ret true
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// error-pattern:this block must not have a result
|
// error-pattern:mismatched types: expected `()` but found `bool`
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// error-pattern:this block must not have a result
|
// error-pattern:mismatched types: expected `()` but found `bool`
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
for i in [0] {
|
for i in [0] {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// error-pattern:this block must not have a result
|
// error-pattern:mismatched types: expected `()` but found `bool`
|
||||||
|
|
||||||
resource r(i: int) {
|
resource r(i: int) {
|
||||||
true
|
true
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// error-pattern:this block must not have a result
|
// error-pattern:mismatched types: expected `()` but found `bool`
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
while true {
|
while true {
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
fn god_exists(a: int) -> bool { be god_exists(a); }
|
fn god_exists(a: int) -> bool { be god_exists(a); }
|
||||||
|
|
||||||
fn f(a: int) -> int { if god_exists(a) { ret 5; } }
|
fn f(a: int) -> int { if god_exists(a) { ret 5; }; }
|
||||||
|
|
||||||
fn main() { f(12); }
|
fn main() { f(12); }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// error-pattern:`if` without `else` can not produce a result
|
// error-pattern:mismatched types: expected `()` but found `bool`
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let a = if true { true };
|
let a = if true { true };
|
||||||
|
|
|
@ -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 true { true { } };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() { }
|
fn main() { }
|
||||||
|
|
8
src/test/pretty/disamb-stmt-expr.rs
Normal file
8
src/test/pretty/disamb-stmt-expr.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// pp-exact
|
||||||
|
|
||||||
|
// Here we check that the parentheses around the body of `wsucc()` are
|
||||||
|
// preserved. They are needed to disambiguate `{ret n+1}; - 0` from
|
||||||
|
// `({ret n+1}-0)`.
|
||||||
|
|
||||||
|
fn wsucc(n: int) -> int { ({ ret n + 1 } - 0); }
|
||||||
|
fn main() { }
|
|
@ -1,2 +1,2 @@
|
||||||
fn wsucc(n: int) -> int { { ret n + 1 } + 0; }
|
fn wsucc(n: int) -> int { ({ ret n + 1 } + 0); }
|
||||||
fn main() { }
|
fn main() { }
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
fn wsucc(n: int) -> int { { ret n + 1 } == 0; }
|
fn wsucc(n: int) -> int { ({ ret n + 1 } == 0); }
|
||||||
fn main() { }
|
fn main() { }
|
||||||
|
|
|
@ -14,6 +14,7 @@ fn test_setenv() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore(reason = "fails periodically on mac")]
|
||||||
fn test_setenv_overwrite() {
|
fn test_setenv_overwrite() {
|
||||||
setenv("NAME2", "1");
|
setenv("NAME2", "1");
|
||||||
setenv("NAME2", "2");
|
setenv("NAME2", "2");
|
||||||
|
@ -23,12 +24,14 @@ fn test_setenv_overwrite() {
|
||||||
// Windows GetEnvironmentVariable requires some extra work to make sure
|
// Windows GetEnvironmentVariable requires some extra work to make sure
|
||||||
// the buffer the variable is copied into is the right size
|
// the buffer the variable is copied into is the right size
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore(reason = "fails periodically on mac")]
|
||||||
fn test_getenv_big() {
|
fn test_getenv_big() {
|
||||||
let s = "";
|
let s = "";
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while i < 100 { s += "aaaaaaaaaa"; i += 1; }
|
while i < 100 { s += "aaaaaaaaaa"; i += 1; }
|
||||||
setenv("NAME3", s);
|
setenv("test_getenv_big", s);
|
||||||
assert (getenv("NAME3") == option::some(s));
|
log(debug, s);
|
||||||
|
assert (getenv("test_getenv_big") == option::some(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue