rustc: Teach rustc to use block results as fn return values. Closes #372
This commit is contained in:
parent
223c7dfb11
commit
9daa00bf83
6 changed files with 141 additions and 3 deletions
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
14
src/test/compile-fail/missing-return2.rs
Normal file
14
src/test/compile-fail/missing-return2.rs
Normal 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() {
|
||||
}
|
8
src/test/run-fail/expr-fn-fail.rs
Normal file
8
src/test/run-fail/expr-fn-fail.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
// xfail-stage0
|
||||
// error-pattern:explicit failure
|
||||
|
||||
fn f() -> ! { fail }
|
||||
|
||||
fn main() {
|
||||
f();
|
||||
}
|
65
src/test/run-pass/expr-fn.rs
Normal file
65
src/test/run-pass/expr-fn.rs
Normal 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();
|
||||
}
|
Loading…
Reference in a new issue