From e241f2996dcf99f3d9fd2f9e277e435782c65a61 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Wed, 24 Aug 2011 17:24:58 -0700 Subject: [PATCH] Allow pure fns to have any return type --- src/comp/middle/ty.rs | 7 ++++ src/comp/middle/typeck.rs | 19 ++++------- src/test/compile-fail/not-a-pred-check.rs | 2 +- src/test/run-pass/non-boolean-pure-fns.rs | 32 +++++++++++++++++++ .../pred-not-bool.rs | 2 +- 5 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 src/test/run-pass/non-boolean-pure-fns.rs rename src/test/{compile-fail => run-pass}/pred-not-bool.rs (82%) diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index 5d448825b890..e65265e640f7 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -55,6 +55,7 @@ export hash_ty; export idx_nil; export is_lval; export is_binopable; +export is_pred_ty; export item_table; export lookup_item_type; export method; @@ -1737,6 +1738,12 @@ fn is_fn_ty(cx: &ctxt, fty: t) -> bool { } } +// Just checks whether it's a fn that returns bool, +// not its purity. +fn is_pred_ty(cx: &ctxt, fty:t) -> bool { + is_fn_ty(cx, fty) && type_is_bool(cx, ty_fn_ret(cx, fty)) +} + fn ty_var_id(cx: &ctxt, typ: t) -> int { alt struct(cx, typ) { ty::ty_var(vid) { ret vid; } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index f282c63861f2..ad199ba5cb3d 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -1715,6 +1715,11 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier, literals or slots */ alt e.node { ast::expr_call(operator, operands) { + if !ty::is_pred_ty(fcx.ccx.tcx, expr_ty(fcx.ccx.tcx, operator)) { + fcx.ccx.tcx.sess.span_fatal(operator.span, + "Operator in constraint has non-boolean return type"); + } + alt operator.node { ast::expr_path(oper_name) { alt fcx.ccx.tcx.def_map.find(operator.id) { @@ -1723,7 +1728,7 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier, } _ { fcx.ccx.tcx.sess.span_fatal(operator.span, - "non-predicate as operator \ + "Impure function as operator \ in constraint"); } } @@ -2596,18 +2601,6 @@ fn check_fn(ccx: &@crate_ctxt, f: &ast::_fn, id: &ast::node_id, mutable fixups: fixups, ccx: ccx}; check_block(fcx, body); - alt decl.purity { - ast::pure_fn. { - - // This just checks that the declared type is bool, and trusts - // that that's the actual return type. - if !ty::type_is_bool(ccx.tcx, fcx.ret_ty) { - ccx.tcx.sess.span_err(body.span, - "Non-boolean return type in pred"); - } - } - _ { } - } // For non-iterator fns, we unify the tail expr's type with the // function result type, if there is a tail expr. diff --git a/src/test/compile-fail/not-a-pred-check.rs b/src/test/compile-fail/not-a-pred-check.rs index 8ba91d0e26e5..9891360af9e6 100644 --- a/src/test/compile-fail/not-a-pred-check.rs +++ b/src/test/compile-fail/not-a-pred-check.rs @@ -1,5 +1,5 @@ // -*- rust -*- -// error-pattern: non-predicate +// error-pattern: Impure function as operator fn f(q: int) -> bool { ret true; } diff --git a/src/test/run-pass/non-boolean-pure-fns.rs b/src/test/run-pass/non-boolean-pure-fns.rs new file mode 100644 index 000000000000..61477258bb4b --- /dev/null +++ b/src/test/run-pass/non-boolean-pure-fns.rs @@ -0,0 +1,32 @@ +use std; + +import std::list::*; + +pure fn pure_length_go<@T>(ls: &list, acc: uint) -> uint { + alt ls { + nil. { acc } + cons(_, tl) { pure_length_go(*tl, acc + 1u) } + } +} + +pure fn pure_length<@T>(ls: &list) -> uint { + pure_length_go(ls, 0u) +} + +pure fn nonempty_list<@T>(ls: &list) -> bool { + pure_length(ls) > 0u +} + + // Of course, the compiler can't take advantage of the + // knowledge that ls is a cons node. Future work. + // Also, this is pretty contrived since nonempty_list + // could be a "tag refinement", if we implement those. +fn safe_head<@T>(ls: &list) : nonempty_list(ls) -> T { car(ls) } + +fn main() { + let mylist = cons(@1u, @nil); + // Again, a way to eliminate such "obvious" checks seems + // desirable. (Tags could have postconditions.) + check(nonempty_list(mylist)); + assert (*(safe_head(mylist)) == 1u); +} \ No newline at end of file diff --git a/src/test/compile-fail/pred-not-bool.rs b/src/test/run-pass/pred-not-bool.rs similarity index 82% rename from src/test/compile-fail/pred-not-bool.rs rename to src/test/run-pass/pred-not-bool.rs index 50344120a98e..1f70cb73f600 100644 --- a/src/test/compile-fail/pred-not-bool.rs +++ b/src/test/run-pass/pred-not-bool.rs @@ -5,6 +5,6 @@ // this checks that a pred with a non-bool return // type is rejected, even if the pred is never used -pred bad(a: int) -> int { ret 37; } +pure fn bad(a: int) -> int { ret 37; } fn main() { }