about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorTim Chevalier <chevalier@alum.wellesley.edu>2011-08-24 17:24:58 -0700
committerTim Chevalier <chevalier@alum.wellesley.edu>2011-08-25 17:23:35 -0700
commite241f2996dcf99f3d9fd2f9e277e435782c65a61 (patch)
treecc86000e235ab08c5b0ac9a3f3b719736df1d068 /src
parent4dd23f24d65e05169594bfcb45b84adfe4e52516 (diff)
downloadrust-e241f2996dcf99f3d9fd2f9e277e435782c65a61.tar.gz
rust-e241f2996dcf99f3d9fd2f9e277e435782c65a61.zip
Allow pure fns to have any return type
Diffstat (limited to 'src')
-rw-r--r--src/comp/middle/ty.rs7
-rw-r--r--src/comp/middle/typeck.rs19
-rw-r--r--src/test/compile-fail/not-a-pred-check.rs2
-rw-r--r--src/test/run-pass/non-boolean-pure-fns.rs32
-rw-r--r--src/test/run-pass/pred-not-bool.rs (renamed from src/test/compile-fail/pred-not-bool.rs)2
5 files changed, 47 insertions, 15 deletions
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index 5d448825b89..e65265e640f 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 f282c63861f..ad199ba5cb3 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 8ba91d0e26e..9891360af9e 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 00000000000..61477258bb4
--- /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<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);
+}
\ 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
index 50344120a98..1f70cb73f60 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() { }