From d07c6e8a0ede3114ebfd8c3ea6cc161cf009f072 Mon Sep 17 00:00:00 2001 From: Lenny222 Date: Thu, 29 Dec 2011 21:24:03 +0100 Subject: [PATCH 1/2] list: use predicate to enforce non-empty requirement --- src/comp/middle/last_use.rs | 6 ++-- src/comp/middle/resolve.rs | 2 ++ src/libstd/list.rs | 35 ++++++++++++++++++++--- src/test/run-pass/non-boolean-pure-fns.rs | 5 +++- src/test/run-pass/unchecked-predicates.rs | 12 ++++++-- src/test/stdtest/list.rs | 28 +++++++++++++----- 6 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/comp/middle/last_use.rs b/src/comp/middle/last_use.rs index 7b1a0b6c0a3fb..75758c2cdf4d7 100644 --- a/src/comp/middle/last_use.rs +++ b/src/comp/middle/last_use.rs @@ -1,7 +1,7 @@ import syntax::{visit, ast_util}; import syntax::ast::*; import syntax::codemap::span; -import std::list::{list, nil, cons, tail}; +import std::list::{is_not_empty, list, nil, cons, tail}; import core::{vec, option}; import std::list; @@ -177,7 +177,9 @@ fn visit_block(tp: block_type, cx: ctx, visit: block()) { visit(); local.second = true; visit(); - cx.blocks = tail(cx.blocks); + let cx_blocks = cx.blocks; + check is_not_empty(cx_blocks); + cx.blocks = tail(cx_blocks); cx.current = join_branches(local.exits); } diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index 9eaaee2cb3650..ceb8e53f7cea8 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -266,6 +266,7 @@ fn map_crate(e: @env, c: @ast::crate) { let imp = follow_import(*e, sc, *path, vi.span); if option::is_some(imp) { let glob = {def: option::get(imp), item: vi}; + check list::is_not_empty(sc); alt list::head(sc) { scope_item(i) { e.mod_map.get(i.id).glob_imports += [glob]; @@ -455,6 +456,7 @@ fn visit_block_with_scope(b: ast::blk, sc: scopes, v: vt) { } fn visit_decl_with_scope(d: @decl, sc: scopes, v: vt) { + check list::is_not_empty(sc); let loc_pos = alt list::head(sc) { scope_block(_, _, pos) { pos } _ { @mutable 0u } diff --git a/src/libstd/list.rs b/src/libstd/list.rs index c6a24d275eb34..f6b46ed7d2e86 100644 --- a/src/libstd/list.rs +++ b/src/libstd/list.rs @@ -97,6 +97,27 @@ fn has(ls: list, elt: T) -> bool { ret false; } +/* +Function: is_empty + +Returns true if the list is empty. +*/ +pure fn is_empty(ls: list) -> bool { + alt ls { + nil. { true } + _ { false } + } +} + +/* +Function: is_not_empty + +Returns true if the list is not empty. +*/ +pure fn is_not_empty(ls: list) -> bool { + ret !is_empty(ls); +} + /* Function: len @@ -112,8 +133,11 @@ Function: tail Returns all but the first element of a list */ -pure fn tail(ls: list) -> list { - alt ls { cons(_, tl) { ret *tl; } nil. { fail "list empty" } } +pure fn tail(ls: list) : is_not_empty(ls) -> list { + alt ls { + cons(_, tl) { ret *tl; } + nil. { fail "list empty" } + } } /* @@ -121,8 +145,11 @@ Function: head Returns the first element of a list */ -pure fn head(ls: list) -> T { - alt ls { cons(hd, _) { ret hd; } nil. { fail "list empty" } } +pure fn head(ls: list) : is_not_empty(ls) -> T { + alt ls { + cons(hd, _) { ret hd; } + nil. { fail "list empty" } + } } /* diff --git a/src/test/run-pass/non-boolean-pure-fns.rs b/src/test/run-pass/non-boolean-pure-fns.rs index 3a85cba61621d..27f3b530154db 100644 --- a/src/test/run-pass/non-boolean-pure-fns.rs +++ b/src/test/run-pass/non-boolean-pure-fns.rs @@ -14,7 +14,10 @@ pure fn nonempty_list(ls: list) -> bool { pure_length(ls) > 0u } // 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(ls: list) : nonempty_list(ls) -> T { head(ls) } +fn safe_head(ls: list) : nonempty_list(ls) -> T { + check is_not_empty(ls); + ret head(ls); +} fn main() { let mylist = cons(@1u, @nil); diff --git a/src/test/run-pass/unchecked-predicates.rs b/src/test/run-pass/unchecked-predicates.rs index a4c80b575499c..035b18f805125 100644 --- a/src/test/run-pass/unchecked-predicates.rs +++ b/src/test/run-pass/unchecked-predicates.rs @@ -1,4 +1,6 @@ // Uses foldl to exhibit the unchecked block syntax. +// TODO: since list's head/tail require the predicate "is_not_empty" now and +// we have unit tests for list, this test might me not necessary anymore? use std; import std::list::*; @@ -6,7 +8,10 @@ import std::list::*; // Can't easily be written as a "pure fn" because there's // no syntax for specifying that f is pure. fn pure_foldl(ls: list, u: U, f: block(T, U) -> U) -> U { - alt ls { nil. { u } cons(hd, tl) { f(hd, pure_foldl(*tl, f(hd, u), f)) } } + alt ls { + nil. { u } + cons(hd, tl) { f(hd, pure_foldl(*tl, f(hd, u), f)) } + } } // Shows how to use an "unchecked" block to call a general @@ -22,7 +27,10 @@ pure fn nonempty_list(ls: list) -> bool { pure_length(ls) > 0u } // 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(ls: list) : nonempty_list(ls) -> T { head(ls) } +fn safe_head(ls: list) : nonempty_list(ls) -> T { + check is_not_empty(ls); + ret head(ls) +} fn main() { let mylist = cons(@1u, @nil); diff --git a/src/test/stdtest/list.rs b/src/test/stdtest/list.rs index 227e8ad299dee..d88e669378a75 100644 --- a/src/test/stdtest/list.rs +++ b/src/test/stdtest/list.rs @@ -2,17 +2,23 @@ import core::*; use std; import std::list; -import std::list::head; -import std::list::tail; -import std::list::from_vec; +import std::list::{from_vec, head, is_not_empty, tail}; import option; #[test] fn test_from_vec() { let l = from_vec([0, 1, 2]); + + check is_not_empty(l); assert (head(l) == 0); - assert (head(tail(l)) == 1); - assert (head(tail(tail(l))) == 2); + + let tail_l = tail(l); + check is_not_empty(tail_l); + assert (head(tail_l) == 1); + + let tail_tail_l = tail(tail_l); + check is_not_empty(tail_tail_l); + assert (head(tail_tail_l) == 2); } #[test] @@ -24,9 +30,17 @@ fn test_from_vec_empty() { #[test] fn test_from_vec_mut() { let l = from_vec([mutable 0, 1, 2]); + + check is_not_empty(l); assert (head(l) == 0); - assert (head(tail(l)) == 1); - assert (head(tail(tail(l))) == 2); + + let tail_l = tail(l); + check is_not_empty(tail_l); + assert (head(tail_l) == 1); + + let tail_tail_l = tail(tail_l); + check is_not_empty(tail_tail_l); + assert (head(tail_tail_l) == 2); } #[test] From ab2a643f278d09c3fcc140ecb4c7cecbf1aff3c4 Mon Sep 17 00:00:00 2001 From: Lenny222 Date: Fri, 30 Dec 2011 10:54:31 +0100 Subject: [PATCH 2/2] add test for list:is_empty() --- src/test/stdtest/list.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/test/stdtest/list.rs b/src/test/stdtest/list.rs index d88e669378a75..b96bc9cd16a56 100644 --- a/src/test/stdtest/list.rs +++ b/src/test/stdtest/list.rs @@ -2,9 +2,24 @@ import core::*; use std; import std::list; -import std::list::{from_vec, head, is_not_empty, tail}; +import std::list::{from_vec, head, is_empty, is_not_empty, tail}; import option; +#[test] +fn test_is_empty() { + let empty : list::list = from_vec([]); + let full1 = from_vec([1]); + let full2 = from_vec(['r', 'u']); + + assert is_empty(empty); + assert !is_empty(full1); + assert !is_empty(full2); + + assert !is_not_empty(empty); + assert is_not_empty(full1); + assert is_not_empty(full2); +} + #[test] fn test_from_vec() { let l = from_vec([0, 1, 2]);