rustc: Teach rustc to use block results as fn return values. Closes #372

This commit is contained in:
Brian Anderson 2011-05-31 15:24:18 -04:00
parent 223c7dfb11
commit 9daa00bf83
6 changed files with 141 additions and 3 deletions

View file

@ -6892,7 +6892,14 @@ fn trans_fn(@local_ctxt cx, &ast::span sp, &ast::_fn f, ast::def_id fid,
auto lltop = bcx.llbb;
auto res = trans_block(bcx, f.body, return);
auto block_ty = node_ann_type(cx.ccx, f.body.node.a);
auto res = if (!ty::type_is_nil(cx.ccx.tcx, block_ty)
&& !ty::type_is_bot(cx.ccx.tcx, block_ty)) {
trans_block(bcx, f.body, save_in(fcx.llretptr))
} else {
trans_block(bcx, f.body, return)
};
if (!is_terminated(res.bcx)) {
// FIXME: until LLVM has a unit type, we are moving around
// C_nil values rather than their void type.

View file

@ -140,6 +140,8 @@ import front::ast::stmt_expr;
import front::ast::block;
import front::ast::block_;
import middle::ty::expr_ann;
import util::common::new_def_hash;
import util::common::decl_lhs;
import util::common::uistr;
@ -696,6 +698,15 @@ fn find_pre_post_block(&fn_ctxt fcx, block b) -> () {
fn find_pre_post_fn(&fn_ctxt fcx, &_fn f) -> () {
find_pre_post_block(fcx, f.body);
// Treat the tail expression as a return statement
alt (f.body.node.expr) {
case (some(?tailexpr)) {
auto tailann = expr_ann(tailexpr);
set_postcond_false(fcx.ccx, tailann);
}
case (none) { /* fallthrough */ }
}
}
fn fn_pre_post(crate_ctxt ccx, &_fn f, &span sp, &ident i, &def_id id,

View file

@ -150,6 +150,11 @@ import front::ast::stmt_expr;
import front::ast::block;
import front::ast::block_;
import middle::ty::expr_ann;
import middle::ty::expr_ty;
import middle::ty::type_is_nil;
import middle::ty::type_is_bot;
import util::common::new_def_hash;
import util::common::decl_lhs;
import util::common::uistr;
@ -753,8 +758,36 @@ fn find_pre_post_state_block(&fn_ctxt fcx, &prestate pres0, &block b)
fn find_pre_post_state_fn(&fn_ctxt fcx, &_fn f) -> bool {
auto num_local_vars = num_locals(fcx.enclosing);
ret find_pre_post_state_block(fcx,
empty_prestate(num_local_vars), f.body);
auto changed = find_pre_post_state_block(fcx,
empty_prestate(num_local_vars), f.body);
// Treat the tail expression as a return statement
alt (f.body.node.expr) {
case (some(?tailexpr)) {
auto tailann = expr_ann(tailexpr);
auto tailty = expr_ty(fcx.ccx.tcx, tailexpr);
// Since blocks and alts and ifs that don't have results
// implicitly result in nil, we have to be careful to not
// interpret nil-typed block results as the result of a
// function with some other return type
if (!type_is_nil(fcx.ccx.tcx, tailty)
&& !type_is_bot(fcx.ccx.tcx, tailty)) {
set_poststate_ann(fcx.ccx, tailann,
false_postcond(num_local_vars));
alt (fcx.enclosing.cf) {
case (noreturn) {
kill_poststate(fcx, tailann, fcx.id);
}
case (_) { }
}
}
}
case (none) { /* fallthrough */ }
}
ret changed;
}
//

View file

@ -0,0 +1,14 @@
// xfail-stage0
// error-pattern: return
fn f() -> int {
// Make sure typestate doesn't interpret this alt expression
// as the function result
alt (true) {
case (true) {
}
}
}
fn main() {
}

View file

@ -0,0 +1,8 @@
// xfail-stage0
// error-pattern:explicit failure
fn f() -> ! { fail }
fn main() {
f();
}

View file

@ -0,0 +1,65 @@
// xfail-stage0
fn test_int() {
fn f() -> int { 10 }
assert (f() == 10);
}
fn test_vec() {
fn f() -> vec[int] { [10, 11] }
assert (f().(1) == 11);
}
fn test_generic() {
fn f[T](&T t) -> T { t }
assert (f(10) == 10);
}
fn test_alt() {
fn f() -> int {
alt (true) {
case (false) { 10 }
case (true) { 20 }
}
}
assert (f() == 20);
}
fn test_if() {
fn f() -> int { if (true) { 10 } else { 20 } }
assert (f() == 10);
}
fn test_block() {
fn f() -> int {{ 10 }}
assert (f() == 10);
}
fn test_ret() {
fn f() -> int {
ret 10 // no semi
}
assert (f() == 10);
}
// From issue #372
fn test_372() {
fn f() -> int { auto x = { 3 }; x }
assert (f() == 3);
}
fn test_nil() {
()
}
fn main() {
test_int();
test_vec();
test_generic();
test_alt();
test_if();
test_block();
test_ret();
test_372();
test_nil();
}