Allow pure fns to have any return type
This commit is contained in:
parent
4dd23f24d6
commit
e241f2996d
5 changed files with 47 additions and 15 deletions
|
@ -55,6 +55,7 @@ export hash_ty;
|
||||||
export idx_nil;
|
export idx_nil;
|
||||||
export is_lval;
|
export is_lval;
|
||||||
export is_binopable;
|
export is_binopable;
|
||||||
|
export is_pred_ty;
|
||||||
export item_table;
|
export item_table;
|
||||||
export lookup_item_type;
|
export lookup_item_type;
|
||||||
export method;
|
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 {
|
fn ty_var_id(cx: &ctxt, typ: t) -> int {
|
||||||
alt struct(cx, typ) {
|
alt struct(cx, typ) {
|
||||||
ty::ty_var(vid) { ret vid; }
|
ty::ty_var(vid) { ret vid; }
|
||||||
|
|
|
@ -1715,6 +1715,11 @@ fn check_expr_with_unifier(fcx: &@fn_ctxt, expr: &@ast::expr, unify: &unifier,
|
||||||
literals or slots */
|
literals or slots */
|
||||||
alt e.node {
|
alt e.node {
|
||||||
ast::expr_call(operator, operands) {
|
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 {
|
alt operator.node {
|
||||||
ast::expr_path(oper_name) {
|
ast::expr_path(oper_name) {
|
||||||
alt fcx.ccx.tcx.def_map.find(operator.id) {
|
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,
|
fcx.ccx.tcx.sess.span_fatal(operator.span,
|
||||||
"non-predicate as operator \
|
"Impure function as operator \
|
||||||
in constraint");
|
in constraint");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2596,18 +2601,6 @@ fn check_fn(ccx: &@crate_ctxt, f: &ast::_fn, id: &ast::node_id,
|
||||||
mutable fixups: fixups,
|
mutable fixups: fixups,
|
||||||
ccx: ccx};
|
ccx: ccx};
|
||||||
check_block(fcx, body);
|
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
|
// For non-iterator fns, we unify the tail expr's type with the
|
||||||
// function result type, if there is a tail expr.
|
// function result type, if there is a tail expr.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// -*- rust -*-
|
// -*- rust -*-
|
||||||
// error-pattern: non-predicate
|
// error-pattern: Impure function as operator
|
||||||
|
|
||||||
fn f(q: int) -> bool { ret true; }
|
fn f(q: int) -> bool { ret true; }
|
||||||
|
|
||||||
|
|
32
src/test/run-pass/non-boolean-pure-fns.rs
Normal file
32
src/test/run-pass/non-boolean-pure-fns.rs
Normal 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);
|
||||||
|
}
|
|
@ -5,6 +5,6 @@
|
||||||
// this checks that a pred with a non-bool return
|
// this checks that a pred with a non-bool return
|
||||||
// type is rejected, even if the pred is never used
|
// 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() { }
|
fn main() { }
|
Loading…
Reference in a new issue