From 9daa00bf83644b6218552f73bab021b624d5f568 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 31 May 2011 15:24:18 -0400 Subject: [PATCH] rustc: Teach rustc to use block results as fn return values. Closes #372 --- src/comp/middle/trans.rs | 9 ++- src/comp/middle/tstate/pre_post_conditions.rs | 11 ++++ src/comp/middle/tstate/states.rs | 37 ++++++++++- src/test/compile-fail/missing-return2.rs | 14 ++++ src/test/run-fail/expr-fn-fail.rs | 8 +++ src/test/run-pass/expr-fn.rs | 65 +++++++++++++++++++ 6 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 src/test/compile-fail/missing-return2.rs create mode 100644 src/test/run-fail/expr-fn-fail.rs create mode 100644 src/test/run-pass/expr-fn.rs diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 619854bc6223..18d4999297ba 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -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. diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs index d78e03eac14c..fc449de71480 100644 --- a/src/comp/middle/tstate/pre_post_conditions.rs +++ b/src/comp/middle/tstate/pre_post_conditions.rs @@ -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, diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs index 9d688e16d521..77231200e8d7 100644 --- a/src/comp/middle/tstate/states.rs +++ b/src/comp/middle/tstate/states.rs @@ -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; } // diff --git a/src/test/compile-fail/missing-return2.rs b/src/test/compile-fail/missing-return2.rs new file mode 100644 index 000000000000..8baa6a3ac18b --- /dev/null +++ b/src/test/compile-fail/missing-return2.rs @@ -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() { +} diff --git a/src/test/run-fail/expr-fn-fail.rs b/src/test/run-fail/expr-fn-fail.rs new file mode 100644 index 000000000000..c328f91046b2 --- /dev/null +++ b/src/test/run-fail/expr-fn-fail.rs @@ -0,0 +1,8 @@ +// xfail-stage0 +// error-pattern:explicit failure + +fn f() -> ! { fail } + +fn main() { + f(); +} diff --git a/src/test/run-pass/expr-fn.rs b/src/test/run-pass/expr-fn.rs new file mode 100644 index 000000000000..75963d6d7972 --- /dev/null +++ b/src/test/run-pass/expr-fn.rs @@ -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(); +}