Allow pure fns to have any return type

This commit is contained in:
Tim Chevalier 2011-08-24 17:24:58 -07:00
parent 4dd23f24d6
commit e241f2996d
5 changed files with 47 additions and 15 deletions

View file

@ -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; }

View file

@ -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.

View file

@ -1,5 +1,5 @@
// -*- rust -*-
// error-pattern: non-predicate
// error-pattern: Impure function as operator
fn f(q: int) -> bool { ret true; }

View file

@ -0,0 +1,32 @@
use std;
import std::list::*;
pure fn pure_length_go<@T>(ls: &list<T>, acc: uint) -> uint {
alt ls {
nil. { acc }
cons(_, tl) { pure_length_go(*tl, acc + 1u) }
}
}
pure fn pure_length<@T>(ls: &list<T>) -> uint {
pure_length_go(ls, 0u)
}
pure fn nonempty_list<@T>(ls: &list<T>) -> 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<T>) : 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);
}

View file

@ -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() { }