Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

list: add "is_not_empty" requirement to "head" and "tail" (analogous to "vec") #1392

Merged
merged 2 commits into from
Jan 3, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/comp/middle/last_use.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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);
}

Expand Down
2 changes: 2 additions & 0 deletions src/comp/middle/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -455,6 +456,7 @@ fn visit_block_with_scope(b: ast::blk, sc: scopes, v: vt<scopes>) {
}

fn visit_decl_with_scope(d: @decl, sc: scopes, v: vt<scopes>) {
check list::is_not_empty(sc);
let loc_pos = alt list::head(sc) {
scope_block(_, _, pos) { pos }
_ { @mutable 0u }
Expand Down
35 changes: 31 additions & 4 deletions src/libstd/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,27 @@ fn has<copy T>(ls: list<T>, elt: T) -> bool {
ret false;
}

/*
Function: is_empty

Returns true if the list is empty.
*/
pure fn is_empty<copy T>(ls: list<T>) -> bool {
alt ls {
nil. { true }
_ { false }
}
}

/*
Function: is_not_empty

Returns true if the list is not empty.
*/
pure fn is_not_empty<copy T>(ls: list<T>) -> bool {
ret !is_empty(ls);
}

/*
Function: len

Expand All @@ -112,17 +133,23 @@ Function: tail

Returns all but the first element of a list
*/
pure fn tail<copy T>(ls: list<T>) -> list<T> {
alt ls { cons(_, tl) { ret *tl; } nil. { fail "list empty" } }
pure fn tail<copy T>(ls: list<T>) : is_not_empty(ls) -> list<T> {
alt ls {
cons(_, tl) { ret *tl; }
nil. { fail "list empty" }
}
}

/*
Function: head

Returns the first element of a list
*/
pure fn head<copy T>(ls: list<T>) -> T {
alt ls { cons(hd, _) { ret hd; } nil. { fail "list empty" } }
pure fn head<copy T>(ls: list<T>) : is_not_empty(ls) -> T {
alt ls {
cons(hd, _) { ret hd; }
nil. { fail "list empty" }
}
}

/*
Expand Down
5 changes: 4 additions & 1 deletion src/test/run-pass/non-boolean-pure-fns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ pure fn nonempty_list<copy T>(ls: list<T>) -> 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<copy T>(ls: list<T>) : nonempty_list(ls) -> T { head(ls) }
fn safe_head<copy T>(ls: list<T>) : nonempty_list(ls) -> T {
check is_not_empty(ls);
ret head(ls);
}

fn main() {
let mylist = cons(@1u, @nil);
Expand Down
12 changes: 10 additions & 2 deletions src/test/run-pass/unchecked-predicates.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// 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::*;

// Can't easily be written as a "pure fn" because there's
// no syntax for specifying that f is pure.
fn pure_foldl<copy T, copy U>(ls: list<T>, 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
Expand All @@ -22,7 +27,10 @@ pure fn nonempty_list<copy T>(ls: list<T>) -> 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<copy T>(ls: list<T>) : nonempty_list(ls) -> T { head(ls) }
fn safe_head<copy T>(ls: list<T>) : nonempty_list(ls) -> T {
check is_not_empty(ls);
ret head(ls)
}

fn main() {
let mylist = cons(@1u, @nil);
Expand Down
43 changes: 36 additions & 7 deletions src/test/stdtest/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,38 @@ 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_empty, is_not_empty, tail};
import option;

#[test]
fn test_is_empty() {
let empty : list::list<int> = 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]);

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]
Expand All @@ -24,9 +45,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]
Expand Down