From 9e6b43fb33c47f99385d6a4c5442eac1f74d273c Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Fri, 6 Jul 2012 15:04:57 -0700 Subject: [PATCH 01/32] Rip out a bunch more append code from trans. --- src/rustc/middle/trans/base.rs | 21 ----------- src/rustc/middle/trans/tvec.rs | 69 ---------------------------------- 2 files changed, 90 deletions(-) diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 83a9847c163b7..377dac6120fde 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1755,28 +1755,7 @@ fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop, _ {} } - // Special case for `+= ~[x]` - alt ty::get(t).struct { - ty::ty_vec(_) { - alt src.node { - ast::expr_vec(args, _) { - ret tvec::trans_append_literal(lhs_res.bcx, - lhs_res.val, t, args); - } - _ { } - } - } - _ { } - } let {bcx, val: rhs_val} = trans_temp_expr(lhs_res.bcx, src); - if ty::type_is_sequence(t) { - alt op { - ast::add { - ret tvec::trans_append(bcx, t, lhs_res.val, rhs_val); - } - _ { } - } - } ret trans_eager_binop(bcx, ex.span, op, Load(bcx, lhs_res.val), t, rhs_val, t, save_in(lhs_res.val)); diff --git a/src/rustc/middle/trans/tvec.rs b/src/rustc/middle/trans/tvec.rs index d3212cd99e060..f7725cdb0771d 100644 --- a/src/rustc/middle/trans/tvec.rs +++ b/src/rustc/middle/trans/tvec.rs @@ -306,75 +306,6 @@ fn trans_estr(bcx: block, s: @str, vstore: ast::vstore, base::store_in_dest(bcx, c, dest) } -fn trans_append(bcx: block, vec_ty: ty::t, lhsptr: ValueRef, - rhs: ValueRef) -> block { - let _icx = bcx.insn_ctxt("tvec::trans_append"); - // Cast to opaque interior vector types if necessary. - let ccx = bcx.ccx(); - let unit_ty = ty::sequence_element_type(ccx.tcx, vec_ty); - let strings = ty::type_is_str(vec_ty); - - let lhs = Load(bcx, lhsptr); - let self_append = ICmp(bcx, lib::llvm::IntEQ, lhs, rhs); - let lfill = get_fill(bcx, get_bodyptr(bcx, lhs)); - let rfill = get_fill(bcx, get_bodyptr(bcx, rhs)); - let mut new_fill = Add(bcx, lfill, rfill); - if strings { new_fill = Sub(bcx, new_fill, C_int(ccx, 1)); } - let opaque_lhs = PointerCast(bcx, lhsptr, - T_ptr(T_ptr(T_i8()))); - Call(bcx, ccx.upcalls.vec_grow, - ~[opaque_lhs, new_fill]); - // Was overwritten if we resized - let lhs = Load(bcx, lhsptr); - let rhs = Select(bcx, self_append, lhs, rhs); - - let lbody = get_bodyptr(bcx, lhs); - - let lhs_data = get_dataptr(bcx, lbody); - let mut lhs_off = lfill; - if strings { lhs_off = Sub(bcx, lhs_off, C_int(ccx, 1)); } - let write_ptr = pointer_add(bcx, lhs_data, lhs_off); - let write_ptr_ptr = do_spill_noroot(bcx, write_ptr); - iter_vec_uniq(bcx, rhs, vec_ty, rfill, |bcx, addr, _ty| { - let write_ptr = Load(bcx, write_ptr_ptr); - let bcx = copy_val(bcx, INIT, write_ptr, - load_if_immediate(bcx, addr, unit_ty), unit_ty); - Store(bcx, InBoundsGEP(bcx, write_ptr, ~[C_int(ccx, 1)]), - write_ptr_ptr); - bcx - }) -} - -fn trans_append_literal(bcx: block, vptrptr: ValueRef, vec_ty: ty::t, - vals: ~[@ast::expr]) -> block { - let _icx = bcx.insn_ctxt("tvec::trans_append_literal"); - let mut bcx = bcx, ccx = bcx.ccx(); - let elt_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); - let elt_llty = type_of::type_of(ccx, elt_ty); - let elt_sz = shape::llsize_of(ccx, elt_llty); - let scratch = base::alloca(bcx, elt_llty); - for vec::each(vals) |val| { - bcx = base::trans_expr_save_in(bcx, val, scratch); - let vptr = get_bodyptr(bcx, Load(bcx, vptrptr)); - let old_fill = get_fill(bcx, vptr); - let new_fill = Add(bcx, old_fill, elt_sz); - let do_grow = ICmp(bcx, lib::llvm::IntUGT, new_fill, - get_alloc(bcx, vptr)); - bcx = do base::with_cond(bcx, do_grow) |bcx| { - let pt = PointerCast(bcx, vptrptr, - T_ptr(T_ptr(T_i8()))); - Call(bcx, ccx.upcalls.vec_grow, ~[pt, new_fill]); - bcx - }; - let vptr = get_bodyptr(bcx, Load(bcx, vptrptr)); - set_fill(bcx, vptr, new_fill); - let targetptr = pointer_add(bcx, get_dataptr(bcx, vptr), - old_fill); - call_memmove(bcx, targetptr, scratch, elt_sz); - } - bcx -} - type val_and_ty_fn = fn@(block, ValueRef, ty::t) -> result; type iter_vec_block = fn(block, ValueRef, ty::t) -> block; From fce064db6b40de3c75714e6a1323eee0726675d6 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 15:10:12 -0700 Subject: [PATCH 02/32] tutorial: Reduce header level for do/for loop sections --- doc/tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index b662a218eb48f..0e54485bf17fb 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1112,7 +1112,7 @@ fn bare_function() { "I am a plain function"; } call_twice(bare_function); ~~~~ -### Do syntax +## Do syntax Closures in Rust are frequently used in combination with higher-order functions to simulate control structures like `if` and @@ -1183,7 +1183,7 @@ do spawn { Empty argument lists can be omitted from `do` expressions. -### For loops +## For loops Most iteration in Rust is done with `for` loops. Like `do`, `for` is a nice syntax for doing control flow with closures. From b925648ac71cfd28298ad994428cafc19f49692b Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 6 Jul 2012 15:15:52 -0700 Subject: [PATCH 03/32] Added a k-nucleotide version that uses pipes. 31% speedup. --- src/libcore/task.rs | 39 +++ src/test/bench/shootout-k-nucleotide-pipes.rs | 250 ++++++++++++++++++ 2 files changed, 289 insertions(+) create mode 100644 src/test/bench/shootout-k-nucleotide-pipes.rs diff --git a/src/libcore/task.rs b/src/libcore/task.rs index 2d2c2660fc880..f41e24c623f70 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -47,6 +47,7 @@ export unsupervise; export run_listener; export spawn; +export spawn_with; export spawn_listener; export spawn_sched; export try; @@ -338,6 +339,28 @@ fn unsupervise(builder: builder) { }); } +fn run_with(-builder: builder, + +arg: A, + +f: fn~(+A)) { + + /*! + * + * Runs a task, while transfering ownership of one argument to the + * child. + * + * This is useful for transfering ownership of noncopyables to + * another task. + * + */ + + let arg = ~mut some(arg); + do run(builder) { + let mut my_arg = none; + my_arg <-> *arg; + f(option::unwrap(my_arg)) + } +} + fn run_listener(-builder: builder, +f: fn~(comm::port)) -> comm::chan { /*! @@ -381,6 +404,22 @@ fn spawn(+f: fn~()) { run(builder(), f); } +fn spawn_with(+arg: A, +f: fn~(+A)) { + /*! + * Runs a new task while providing a channel from the parent to the child + * + * Sets up a communication channel from the current task to the new + * child task, passes the port to child's body, and returns a channel + * linked to the port to the parent. + * + * This encapsulates some boilerplate handshaking logic that would + * otherwise be required to establish communication from the parent + * to the child. + */ + + run_with(builder(), arg, f) +} + fn spawn_listener(+f: fn~(comm::port)) -> comm::chan { /*! * Runs a new task while providing a channel from the parent to the child diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs new file mode 100644 index 0000000000000..7212bbc765b1a --- /dev/null +++ b/src/test/bench/shootout-k-nucleotide-pipes.rs @@ -0,0 +1,250 @@ +// xfail-pretty + +// multi tasking k-nucleotide + +import io::reader_util; + +use std; +import std::map; +import std::map::hashmap; +import std::sort; + +import stream::{stream, chan, port}; + +// After a snapshot, this should move into core, or std. +mod stream { + import option::unwrap; + + proto! streamp { + open:send { + data(T) -> open + } + } + + type chan = { mut endp: option> }; + type port = { mut endp: option> }; + + fn stream() -> (chan, port) { + let (c, s) = streamp::init(); + ({ mut endp: some(c) }, { mut endp: some(s) }) + } + + impl chan for chan { + fn send(+x: T) { + let mut endp = none; + endp <-> self.endp; + self.endp = some( + streamp::client::data(unwrap(endp), x)) + } + } + + impl port for port { + fn recv() -> T { + let mut endp = none; + endp <-> self.endp; + let streamp::data(x, endp) = unwrap( + pipes::recv(unwrap(endp))); + self.endp = some(endp); + x + } + } +} + +// given a map, print a sorted version of it +fn sort_and_fmt(mm: hashmap<~[u8], uint>, total: uint) -> str { + fn pct(xx: uint, yy: uint) -> float { + ret (xx as float) * 100f / (yy as float); + } + + fn le_by_val(kv0: (TT,UU), kv1: (TT,UU)) -> bool { + let (_, v0) = kv0; + let (_, v1) = kv1; + ret v0 >= v1; + } + + fn le_by_key(kv0: (TT,UU), kv1: (TT,UU)) -> bool { + let (k0, _) = kv0; + let (k1, _) = kv1; + ret k0 <= k1; + } + + // sort by key, then by value + fn sortKV(orig: ~[(TT,UU)]) -> ~[(TT,UU)] { + ret sort::merge_sort(le_by_val, sort::merge_sort(le_by_key, orig)); + } + + let mut pairs = ~[]; + + // map -> [(k,%)] + mm.each(fn&(key: ~[u8], val: uint) -> bool { + vec::push(pairs, (key, pct(val, total))); + ret true; + }); + + let pairs_sorted = sortKV(pairs); + + let mut buffer = ""; + + pairs_sorted.each(fn&(kv: (~[u8], float)) -> bool unsafe { + let (k,v) = kv; + buffer += (#fmt["%s %0.3f\n", str::to_upper(str::unsafe::from_bytes(k)), v]); + ret true; + }); + + ret buffer; +} + +// given a map, search for the frequency of a pattern +fn find(mm: hashmap<~[u8], uint>, key: str) -> uint { + alt mm.find(str::bytes(str::to_lower(key))) { + option::none { ret 0u; } + option::some(num) { ret num; } + } +} + +// given a map, increment the counter for a key +fn update_freq(mm: hashmap<~[u8], uint>, key: &[u8]) { + let key = vec::slice(key, 0, key.len()); + alt mm.find(key) { + option::none { mm.insert(key, 1u ); } + option::some(val) { mm.insert(key, 1u + val); } + } +} + +// given a ~[u8], for each window call a function +// i.e., for "hello" and windows of size four, +// run it("hell") and it("ello"), then return "llo" +fn windows_with_carry(bb: ~[const u8], nn: uint, + it: fn(window: &[u8])) -> ~[u8] { + let mut ii = 0u; + + let len = vec::len(bb); + while ii < len - (nn - 1u) { + it(vec::view(bb, ii, ii+nn)); + ii += 1u; + } + + ret vec::slice(bb, len - (nn - 1u), len); +} + +fn make_sequence_processor(sz: uint, from_parent: stream::port<~[u8]>, + to_parent: stream::chan) { + + let freqs: hashmap<~[u8], uint> = map::bytes_hash(); + let mut carry: ~[u8] = ~[]; + let mut total: uint = 0u; + + let mut line: ~[u8]; + + loop { + + line = from_parent.recv(); + if line == ~[] { break; } + + carry = windows_with_carry(carry + line, sz, |window| { + update_freq(freqs, window); + total += 1u; + }); + } + + let buffer = alt sz { + 1u { sort_and_fmt(freqs, total) } + 2u { sort_and_fmt(freqs, total) } + 3u { #fmt["%u\t%s", find(freqs, "GGT"), "GGT"] } + 4u { #fmt["%u\t%s", find(freqs, "GGTA"), "GGTA"] } + 6u { #fmt["%u\t%s", find(freqs, "GGTATT"), "GGTATT"] } + 12u { #fmt["%u\t%s", find(freqs, "GGTATTTTAATT"), "GGTATTTTAATT"] } + 18u { #fmt["%u\t%s", find(freqs, "GGTATTTTAATTTATAGT"), "GGTATTTTAATTTATAGT"] } + _ { "" } + }; + + //comm::send(to_parent, #fmt["yay{%u}", sz]); + to_parent.send(buffer); +} + +// given a FASTA file on stdin, process sequence THREE +fn main(args: ~[str]) { + let rdr = if os::getenv("RUST_BENCH").is_some() { + // FIXME: Using this compile-time env variable is a crummy way to + // get to this massive data set, but #include_bin chokes on it (#2598) + let path = path::connect( + #env("CFG_SRC_DIR"), + "src/test/bench/shootout-k-nucleotide.data" + ); + result::get(io::file_reader(path)) + } else { + io::stdin() + }; + + + + // initialize each sequence sorter + let sizes = ~[1u,2u,3u,4u,6u,12u,18u]; + let streams = vec::map(sizes, |_sz| some(stream())); + let streams = vec::to_mut(streams); + let mut from_child = ~[]; + let to_child = vec::mapi(sizes, |ii, sz| { + let mut stream = none; + stream <-> streams[ii]; + let (to_parent_, from_child_) = option::unwrap(stream); + + vec::push(from_child, from_child_); + + let (to_child, from_parent) = stream::stream(); + + do task::spawn_with(from_parent) |from_parent| { + make_sequence_processor(sz, from_parent, to_parent_); + }; + + to_child + }); + + + // latch stores true after we've started + // reading the sequence of interest + let mut proc_mode = false; + + while !rdr.eof() { + let line: str = rdr.read_line(); + + if str::len(line) == 0u { cont; } + + alt (line[0], proc_mode) { + + // start processing if this is the one + ('>' as u8, false) { + alt str::find_str_from(line, "THREE", 1u) { + option::some(_) { proc_mode = true; } + option::none { } + } + } + + // break our processing + ('>' as u8, true) { break; } + + // process the sequence for k-mers + (_, true) { + let line_bytes = str::bytes(line); + + for sizes.eachi |ii, _sz| { + let mut lb = line_bytes; + to_child[ii].send(lb); + } + } + + // whatever + _ { } + } + } + + // finish... + for sizes.eachi |ii, _sz| { + to_child[ii].send(~[]); + } + + // now fetch and print result messages + for sizes.eachi |ii, _sz| { + io::println(from_child[ii].recv()); + } +} + From e20f63d095cdafe378821779fcf9f11bc3cfce78 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 6 Jul 2012 13:42:06 -0700 Subject: [PATCH 04/32] Bank protocol example from blog post --- src/test/run-pass/pipe-bank-proto.rs | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/test/run-pass/pipe-bank-proto.rs diff --git a/src/test/run-pass/pipe-bank-proto.rs b/src/test/run-pass/pipe-bank-proto.rs new file mode 100644 index 0000000000000..e6a4a011b30d2 --- /dev/null +++ b/src/test/run-pass/pipe-bank-proto.rs @@ -0,0 +1,70 @@ +// xfail-pretty + +// An example of the bank protocol from eholk's blog post. +// +// http://theincredibleholk.wordpress.com/2012/07/06/rusty-pipes/ + +import pipes::recv; + +type username = str; +type password = str; +type money = float; +type amount = float; + +proto! bank { + login:send { + login(username, password) -> login_response + } + + login_response:recv { + ok -> connected, + invalid -> login + } + + connected:send { + deposit(money) -> connected, + withdrawal(amount) -> withdrawal_response + } + + withdrawal_response:recv { + money(money) -> connected, + insufficient_funds -> connected + } +} + +fn macros() { + #macro[ + [#move[x], + unsafe { let y <- *ptr::addr_of(x); y }] + ]; +} + +fn bank_client(+bank: bank::client::login) { + import bank::*; + + let bank = client::login(bank, "theincredibleholk", "1234"); + let bank = alt recv(bank) { + some(ok(connected)) { + #move(connected) + } + some(invalid(_)) { fail "login unsuccessful" } + none { fail "bank closed the connection" } + }; + + let bank = client::deposit(bank, 100.00); + let bank = client::withdrawal(bank, 50.00); + alt recv(bank) { + some(money(m, _)) { + io::println("Yay! I got money!"); + } + some(insufficient_funds(_)) { + fail "someone stole my money" + } + none { + fail "bank closed the connection" + } + } +} + +fn main() { +} \ No newline at end of file From ceac155211e8330782024f1c6e8e10cf03245a8d Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Fri, 6 Jul 2012 15:46:31 -0700 Subject: [PATCH 05/32] For #2229, recognize 'again' in place of 'cont', final change pending snapshot. --- doc/rust.md | 16 ++++++++-------- doc/tutorial.md | 8 ++++---- src/etc/emacs/rust-mode.el | 4 ++-- src/fuzzer/fuzzer.rs | 2 +- src/libsyntax/ast.rs | 2 +- src/libsyntax/fold.rs | 2 +- src/libsyntax/parse/parser.rs | 5 +++-- src/libsyntax/parse/token.rs | 3 +-- src/libsyntax/print/pprust.rs | 2 +- src/libsyntax/visit.rs | 2 +- src/rustc/middle/borrowck/categorization.rs | 2 +- src/rustc/middle/check_loop.rs | 2 +- src/rustc/middle/liveness.rs | 6 +++--- src/rustc/middle/trans/base.rs | 2 +- src/rustc/middle/trans/type_use.rs | 2 +- src/rustc/middle/tstate/pre_post_conditions.rs | 2 +- src/rustc/middle/tstate/states.rs | 2 +- src/rustc/middle/typeck/check.rs | 2 +- src/rustc/util/common.rs | 2 +- 19 files changed, 34 insertions(+), 34 deletions(-) diff --git a/doc/rust.md b/doc/rust.md index 074913c8bf1e6..f002393b71ace 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -209,9 +209,9 @@ import export use mod The keywords in [source files](#source-files) are the following strings: ~~~~~~~~ {.keyword} -alt assert +alt again assert break -check claim class const cont copy +check claim class const copy drop else enum export extern fail false fn for @@ -2034,19 +2034,19 @@ break_expr : "break" ; Executing a `break` expression immediately terminates the innermost loop enclosing it. It is only permitted in the body of a loop. -### Continue expressions +### Again expressions ~~~~~~~~{.ebnf .gram} -break_expr : "cont" ; +again_expr : "again" ; ~~~~~~~~ -Evaluating a `cont` expression immediately terminates the current iteration of +Evaluating an `again` expression immediately terminates the current iteration of the innermost loop enclosing it, returning control to the loop *head*. In the case of a `while` loop, the head is the conditional expression controlling the -loop. In the case of a `for` loop, the head is the vector-element increment -controlling the loop. +loop. In the case of a `for` loop, the head is the call-expression controlling +the loop. -A `cont` expression is only permitted in the body of a loop. +An `again` expression is only permitted in the body of a loop. ### For expressions diff --git a/doc/tutorial.md b/doc/tutorial.md index 0e54485bf17fb..149afee960f05 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -783,7 +783,7 @@ a specific value, are not allowed. `while` produces a loop that runs as long as its given condition (which must have type `bool`) evaluates to true. Inside a loop, the -keyword `break` can be used to abort the loop, and `cont` can be used +keyword `break` can be used to abort the loop, and `again` can be used to abort the current iteration and continue with the next. ~~~~ @@ -1187,7 +1187,7 @@ Empty argument lists can be omitted from `do` expressions. Most iteration in Rust is done with `for` loops. Like `do`, `for` is a nice syntax for doing control flow with closures. -Additionally, within a `for` loop, `break, `cont`, and `ret` +Additionally, within a `for` loop, `break, `again`, and `ret` work just as they do with `while` and `loop`. Consider again our `each` function, this time improved to @@ -1221,8 +1221,8 @@ each(~[2, 4, 8, 5, 16], |n| { With `for`, functions like `each` can be treated more like builtin looping structures. When calling `each` in a `for` loop, instead of returning `false` to break -out of the loop, you just write `break`. To continue -to the next iteration, write `cont`. +out of the loop, you just write `break`. To skip ahead +to the next iteration, write `again`. ~~~~ # import each = vec::each; diff --git a/src/etc/emacs/rust-mode.el b/src/etc/emacs/rust-mode.el index 86e5f867cbaa3..a9691a836cb67 100644 --- a/src/etc/emacs/rust-mode.el +++ b/src/etc/emacs/rust-mode.el @@ -56,9 +56,9 @@ "trait" "fn" "enum" "iface" "impl")) (puthash word 'def table)) - (dolist (word '("assert" + (dolist (word '("again" "assert" "break" - "check" "claim" "cont" "copy" + "check" "claim" "copy" "do" "drop" "else" "export" "extern" "fail" "for" diff --git a/src/fuzzer/fuzzer.rs b/src/fuzzer/fuzzer.rs index e6a7a9fcfe6f8..4faf7a2f48d0c 100644 --- a/src/fuzzer/fuzzer.rs +++ b/src/fuzzer/fuzzer.rs @@ -42,7 +42,7 @@ fn common_exprs() -> ~[ast::expr] { } ~[dse(ast::expr_break), - dse(ast::expr_cont), + dse(ast::expr_again), dse(ast::expr_fail(option::none)), dse(ast::expr_fail(option::some( @dse(ast::expr_lit(@dsl(ast::lit_str(@"boo"))))))), diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 4c0be731df4ba..1941d809d7b87 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -336,7 +336,7 @@ enum expr_ { expr_addr_of(mutability, @expr), expr_fail(option<@expr>), expr_break, - expr_cont, + expr_again, expr_ret(option<@expr>), expr_log(int, @expr, @expr), diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 75977ba616931..7f83b16b8ab71 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -464,7 +464,7 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ { } expr_path(pth) { expr_path(fld.fold_path(pth)) } expr_fail(e) { expr_fail(option::map(e, fld.fold_expr)) } - expr_break | expr_cont { copy e } + expr_break | expr_again { copy e } expr_ret(e) { expr_ret(option::map(e, fld.fold_expr)) } expr_log(i, lv, e) { expr_log(i, fld.fold_expr(lv), fld.fold_expr(e)) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2f4fe783b4903..3f430f8a98896 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -966,8 +966,9 @@ class parser { } else if self.eat_keyword("break") { ex = expr_break; hi = self.span.hi; - } else if self.eat_keyword("cont") { - ex = expr_cont; + } else if self.eat_keyword("cont") || + self.eat_keyword("again") { + ex = expr_again; hi = self.span.hi; } else if self.eat_keyword("copy") { let e = self.parse_expr(); diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index dbc1e8b34e34d..b3fad7e9ffd94 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -304,8 +304,7 @@ fn contextual_keyword_table() -> hashmap { fn restricted_keyword_table() -> hashmap { let words = str_hash(); let keys = ~[ - "alt", - "assert", + "alt", "again", "assert", "break", "check", "claim", "class", "const", "cont", "copy", "do", "drop", diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 5220ecdce9363..f7acf9ea68803 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1063,7 +1063,7 @@ fn print_expr(s: ps, &&expr: @ast::expr) { } } ast::expr_break { word(s.s, "break"); } - ast::expr_cont { word(s.s, "cont"); } + ast::expr_again { word(s.s, "again"); } ast::expr_ret(result) { word(s.s, "ret"); alt result { diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index ed54ad3308b0b..0a7e757bbba89 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -421,7 +421,7 @@ fn visit_expr(ex: @expr, e: E, v: vt) { expr_path(p) { visit_path(p, e, v); } expr_fail(eo) { visit_expr_opt(eo, e, v); } expr_break { } - expr_cont { } + expr_again { } expr_ret(eo) { visit_expr_opt(eo, e, v); } expr_log(_, lv, x) { v.visit_expr(lv, e, v); diff --git a/src/rustc/middle/borrowck/categorization.rs b/src/rustc/middle/borrowck/categorization.rs index deccf0af2b46d..1a2b4d7834eca 100644 --- a/src/rustc/middle/borrowck/categorization.rs +++ b/src/rustc/middle/borrowck/categorization.rs @@ -176,7 +176,7 @@ impl public_methods for borrowck_ctxt { ast::expr_new(*) | ast::expr_binary(*) | ast::expr_while(*) | ast::expr_block(*) | ast::expr_loop(*) | ast::expr_alt(*) | ast::expr_lit(*) | ast::expr_break | ast::expr_mac(*) | - ast::expr_cont | ast::expr_rec(*) { + ast::expr_again | ast::expr_rec(*) { ret self.cat_rvalue(expr, expr_ty); } } diff --git a/src/rustc/middle/check_loop.rs b/src/rustc/middle/check_loop.rs index 44fbdaef7ce40..e8e2c57a8b227 100644 --- a/src/rustc/middle/check_loop.rs +++ b/src/rustc/middle/check_loop.rs @@ -33,7 +33,7 @@ fn check_crate(tcx: ty::ctxt, crate: @crate) { tcx.sess.span_err(e.span, "`break` outside of loop"); } } - expr_cont { + expr_again { if !cx.in_loop { tcx.sess.span_err(e.span, "`cont` outside of loop"); } diff --git a/src/rustc/middle/liveness.rs b/src/rustc/middle/liveness.rs index 2f87f6d55de79..50ab6b4b627bf 100644 --- a/src/rustc/middle/liveness.rs +++ b/src/rustc/middle/liveness.rs @@ -470,7 +470,7 @@ fn visit_expr(expr: @expr, &&self: @ir_maps, vt: vt<@ir_maps>) { expr_assert(*) | expr_check(*) | expr_addr_of(*) | expr_copy(*) | expr_loop_body(*) | expr_do_body(*) | expr_cast(*) | expr_unary(*) | expr_fail(*) | - expr_break | expr_cont | expr_lit(_) | expr_ret(*) | + expr_break | expr_again | expr_lit(_) | expr_ret(*) | expr_block(*) | expr_move(*) | expr_assign(*) | expr_swap(*) | expr_assign_op(*) | expr_mac(*) { visit::visit_expr(expr, self, vt); @@ -1009,7 +1009,7 @@ class liveness { self.break_ln } - expr_cont { + expr_again { if !self.cont_ln.is_valid() { self.tcx.sess.span_bug( expr.span, "cont with invalid cont_ln"); @@ -1457,7 +1457,7 @@ fn check_expr(expr: @expr, &&self: @liveness, vt: vt<@liveness>) { expr_assert(*) | expr_check(*) | expr_copy(*) | expr_loop_body(*) | expr_do_body(*) | expr_cast(*) | expr_unary(*) | expr_fail(*) | - expr_ret(*) | expr_break | expr_cont | expr_lit(_) | + expr_ret(*) | expr_break | expr_again | expr_lit(_) | expr_block(*) | expr_swap(*) | expr_mac(*) | expr_addr_of(*) { visit::visit_expr(expr, self, vt); } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 377dac6120fde..459b14038450e 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -3644,7 +3644,7 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block { assert dest == ignore; ret trans_break(bcx); } - ast::expr_cont { + ast::expr_again { assert dest == ignore; ret trans_cont(bcx); } diff --git a/src/rustc/middle/trans/type_use.rs b/src/rustc/middle/trans/type_use.rs index c8ff5ef4c1649..386bf06ef1434 100644 --- a/src/rustc/middle/trans/type_use.rs +++ b/src/rustc/middle/trans/type_use.rs @@ -242,7 +242,7 @@ fn mark_for_expr(cx: ctx, e: @expr) { }) } expr_alt(_, _, _) | expr_block(_) | expr_if(_, _, _) | - expr_while(_, _) | expr_fail(_) | expr_break | expr_cont | + expr_while(_, _) | expr_fail(_) | expr_break | expr_again | expr_unary(_, _) | expr_lit(_) | expr_assert(_) | expr_check(_, _) | expr_if_check(_, _, _) | expr_mac(_) | expr_addr_of(_, _) | expr_ret(_) | expr_loop(_) | diff --git a/src/rustc/middle/tstate/pre_post_conditions.rs b/src/rustc/middle/tstate/pre_post_conditions.rs index 545f6ad6aa97c..c2c8810ec1bb1 100644 --- a/src/rustc/middle/tstate/pre_post_conditions.rs +++ b/src/rustc/middle/tstate/pre_post_conditions.rs @@ -446,7 +446,7 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) { join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check); } expr_break { clear_pp(expr_pp(fcx.ccx, e)); } - expr_cont { clear_pp(expr_pp(fcx.ccx, e)); } + expr_again { clear_pp(expr_pp(fcx.ccx, e)); } expr_mac(_) { fcx.ccx.tcx.sess.bug("unexpanded macro"); } } } diff --git a/src/rustc/middle/tstate/states.rs b/src/rustc/middle/tstate/states.rs index adf1efc85626f..f6cf240eeca34 100644 --- a/src/rustc/middle/tstate/states.rs +++ b/src/rustc/middle/tstate/states.rs @@ -498,7 +498,7 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool { ret join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check, pres); } expr_break { ret pure_exp(fcx.ccx, e.id, pres); } - expr_cont { ret pure_exp(fcx.ccx, e.id, pres); } + expr_again { ret pure_exp(fcx.ccx, e.id, pres); } } } diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index 647498a97e1d3..a983240489ac8 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -1220,7 +1220,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, fcx.write_bot(id); } ast::expr_break { fcx.write_bot(id); bot = true; } - ast::expr_cont { fcx.write_bot(id); bot = true; } + ast::expr_again { fcx.write_bot(id); bot = true; } ast::expr_ret(expr_opt) { bot = true; let ret_ty = alt fcx.indirect_ret_ty { diff --git a/src/rustc/util/common.rs b/src/rustc/util/common.rs index 6a594879d1d41..26b3077210511 100644 --- a/src/rustc/util/common.rs +++ b/src/rustc/util/common.rs @@ -57,7 +57,7 @@ fn loop_query(b: ast::blk, p: fn@(ast::expr_) -> bool) -> bool { fn has_nonlocal_exits(b: ast::blk) -> bool { do loop_query(b) |e| { alt e { - ast::expr_break | ast::expr_cont { true } + ast::expr_break | ast::expr_again { true } _ { false }}} } From 038f9255863d4e07805d8bbefa25e0b61ddfcad0 Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Fri, 6 Jul 2012 15:50:50 -0700 Subject: [PATCH 06/32] Be less eager about implicit borrowing when doing method resolution. Closes #2796. --- src/rustc/middle/typeck/check/method.rs | 39 ++++++++++++--------- src/test/run-pass/assignability-iface.rs | 43 ++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 src/test/run-pass/assignability-iface.rs diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index 709db8733d584..b91f9778e1c84 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -77,16 +77,20 @@ class lookup { // loop for impls in scope. Note: I don't love these // semantics, but that's what we had so I am preserving // it. - if self.candidates.len() > 0u { - break; - } + if self.candidates.len() > 0u { break; } + + // now look for impls in scope, but don't look for impls that + // would require doing an implicit borrow of the lhs. + self.add_candidates_from_scope(false); - self.add_candidates_from_scope(); + // if we found anything, stop before trying borrows + if self.candidates.len() > 0u { break; } + + // now look for impls in scope that might require a borrow + self.add_candidates_from_scope(true); // if we found anything, stop before attempting auto-deref. - if self.candidates.len() > 0u { - break; - } + if self.candidates.len() > 0u { break; } // check whether we can autoderef and if so loop around again. alt ty::deref(self.tcx(), self.self_ty, false) { @@ -290,7 +294,7 @@ class lookup { */ } - fn add_candidates_from_scope() { + fn add_candidates_from_scope(use_assignability: bool) { let impls_vecs = self.fcx.ccx.impl_map.get(self.expr.id); let mut added_any = false; @@ -306,13 +310,18 @@ class lookup { let {substs: impl_substs, ty: impl_ty} = impl_self_ty(self.fcx, im.did); - // if we can assign the caller to the callee, that's a - // potential match. Collect those in the vector. - let can_assign = self.fcx.can_mk_assignty( - self.self_expr, self.borrow_scope, - self.self_ty, impl_ty); - #debug["can_assign = %?", can_assign]; - alt can_assign { + // Depending on our argument, we find potential + // matches either by checking subtypability or + // type assignability. Collect the matches. + let matches = if use_assignability { + self.fcx.can_mk_assignty( + self.self_expr, self.borrow_scope, + self.self_ty, impl_ty) + } else { + self.fcx.can_mk_subty(self.self_ty, impl_ty) + }; + #debug["matches = %?", matches]; + alt matches { result::err(_) { /* keep looking */ } result::ok(_) { if !self.candidate_impls.contains_key(im.did) { diff --git a/src/test/run-pass/assignability-iface.rs b/src/test/run-pass/assignability-iface.rs new file mode 100644 index 0000000000000..47cf7535a6e08 --- /dev/null +++ b/src/test/run-pass/assignability-iface.rs @@ -0,0 +1,43 @@ +// Tests that type assignability is used to search for instances when +// making method calls, but only if there aren't any matches without +// it. + +iface iterable { + fn iterate(blk: fn(A) -> bool); +} + +impl vec/& of iterable for &[const A] { + fn iterate(f: fn(A) -> bool) { + vec::each(self, f); + } +} + +impl vec of iterable for ~[const A] { + fn iterate(f: fn(A) -> bool) { + vec::each(self, f); + } +} + +fn length>(x: T) -> uint { + let mut len = 0; + for x.iterate() |_y| { len += 1 } + ret len; +} + +fn main() { + let x = ~[0,1,2,3]; + // Call a method + for x.iterate() |y| { assert x[y] == y; } + // Call a parameterized function + assert length(x) == vec::len(x); + // Call a parameterized function, with type arguments that require + // a borrow + assert length::(x) == vec::len(x); + + // Now try it with a type that *needs* to be borrowed + let z = [0,1,2,3]/_; + // Call a method + for z.iterate() |y| { assert z[y] == y; } + // Call a parameterized function + assert length::(z) == vec::len(z); +} From 23c73360ca2768ce1fd7ab2be2a2210bb9f728a9 Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Thu, 5 Jul 2012 17:13:32 -0700 Subject: [PATCH 07/32] Fix the indenter script to know about the annoying ~ in the front of log strings. --- src/etc/indenter | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/etc/indenter b/src/etc/indenter index f2e41da9b4e44..017cb926981fb 100755 --- a/src/etc/indenter +++ b/src/etc/indenter @@ -4,13 +4,13 @@ use warnings; my $indent = 0; while (<>) { - if (/^rust: ">>/) { + if (/^rust: ~">>/) { $indent += 1; } printf "%03d %s%s", $indent, (" " x $indent), $_; - if (/^rust: "< Date: Fri, 6 Jul 2012 16:03:43 -0700 Subject: [PATCH 08/32] First step on #2826, accept ^ for ty_ptr. --- src/libsyntax/parse/parser.rs | 3 ++- src/libsyntax/print/pprust.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3f430f8a98896..c94e2acbb2b44 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -463,7 +463,8 @@ class parser { } mt { ty_uniq(mt) } } - } else if self.token == token::BINOP(token::STAR) { + } else if self.token == token::BINOP(token::STAR) || + self.token == token::BINOP(token::CARET) { self.bump(); ty_ptr(self.parse_mt()) } else if self.token == token::LBRACE { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index f7acf9ea68803..4e6b47db1b2ed 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -341,7 +341,7 @@ fn print_type_ex(s: ps, &&ty: @ast::ty, print_colons: bool) { print_type(s, mt.ty); word(s.s, "]"); } - ast::ty_ptr(mt) { word(s.s, "*"); print_mt(s, mt); } + ast::ty_ptr(mt) { word(s.s, "^"); print_mt(s, mt); } ast::ty_rptr(region, mt) { alt region.node { ast::re_anon { word(s.s, "&"); } From 57698fec6ca1561577a5a7b5ef13fe74d82c78a5 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 17:07:12 -0700 Subject: [PATCH 09/32] tutorial: Remove some trivia --- doc/tutorial.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 149afee960f05..5b29a8cd0593d 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -83,7 +83,7 @@ fn main() { // Open a channel to receive game results do listen |result_from_game| { - let times = 10; + let = 10; let player1 = "graydon"; let player2 = "patrick"; @@ -348,9 +348,6 @@ Rust identifiers must start with an alphabetic character or an underscore, and after that may contain any alphanumeric character, and more underscores. -***Note:*** The parser doesn't currently recognize non-ascii alphabetic -characters. This is a bug that will eventually be fixed. - The double-colon (`::`) is used as a module separator, so `io::println` means 'the thing named `println` in the module named `io`'. From 4bb13c69d63c47dfb4f9d1b2ec6ab56f1b7bb698 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 17:18:11 -0700 Subject: [PATCH 10/32] Add 'class' and 'new' to codemirror-rust.js --- doc/lib/codemirror-rust.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/lib/codemirror-rust.js b/doc/lib/codemirror-rust.js index 900180c5eb5e3..6c05ee252b11d 100644 --- a/doc/lib/codemirror-rust.js +++ b/doc/lib/codemirror-rust.js @@ -5,11 +5,11 @@ CodeMirror.defineMode("rust", function() { "do": "else-style", "ret": "else-style", "fail": "else-style", "break": "atom", "cont": "atom", "const": "let", "resource": "fn", "let": "let", "fn": "fn", "for": "for", "alt": "alt", "iface": "iface", - "impl": "impl", "type": "type", "enum": "enum", "mod": "mod", + "impl": "impl", "type": "type", "enum": "enum", "class": "atom", "mod": "mod", "as": "op", "true": "atom", "false": "atom", "assert": "op", "check": "op", "claim": "op", "extern": "ignore", "unsafe": "ignore", "import": "else-style", "export": "else-style", "copy": "op", "log": "op", "log_err": "op", - "use": "op", "bind": "op", "self": "atom" + "use": "op", "bind": "op", "self": "atom", "new": "atom" }; var typeKeywords = function() { var keywords = {"fn": "fn"}; From ad05996223d488f6b3ec904868d3424e8ef7ba30 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 18:09:22 -0700 Subject: [PATCH 11/32] tutorial: Fix a test-breaking typo --- doc/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 5b29a8cd0593d..4688fc4726895 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -83,7 +83,7 @@ fn main() { // Open a channel to receive game results do listen |result_from_game| { - let = 10; + let times = 10; let player1 = "graydon"; let player2 = "patrick"; From af199f2edb0e5a85af7fdd2b8b85173f0928b512 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 18:09:59 -0700 Subject: [PATCH 12/32] tutorial: Expand the section on datatypes --- doc/tutorial.md | 88 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 27 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 4688fc4726895..301ca6cf5ccfa 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1252,17 +1252,32 @@ fn contains(v: ~[int], elt: int) -> bool { # Datatypes -Rust datatypes are, by default, immutable. The core datatypes of Rust -are structural records and 'enums' (tagged unions, algebraic data -types). +The core datatypes of Rust are structural records, enums (tagged +unions, algebraic data types), and classes. They are immutable +by default. ~~~~ type point = {x: float, y: float}; + enum shape { circle(point, float), rectangle(point, point) } -let my_shape = circle({x: 0.0, y: 0.0}, 10.0); + +class drawing { + let mut shapes: [shape]; + + new() { + self.shapes = []; + } + + fn add_shape(new_shape: shape) { + self.shapes += [new_shape]; + } +} + +let my_drawing = drawing(); +my_drawing.add_shape(circle({x: 0.0, y: 0.0}, 10.0)); ~~~~ ## Records @@ -1271,12 +1286,10 @@ Rust record types are written `{field1: T1, field2: T2 [, ...]}`, where `T1`, `T2`, ... denote types. Record literals are written in the same way, but with expressions instead of types. They are quite similar to C structs, and even laid out the same way in memory (so you -can read from a Rust struct in C, and vice-versa). - -The dot operator is used to access record fields (`mypoint.x`). +can read from a Rust struct in C, and vice-versa). The dot operator is +used to access record fields (`mypoint.x`). -Fields that you want to mutate must be explicitly marked as such. For -example... +Fields that you want to mutate must be explicitly marked `mut`. ~~~~ type stack = {content: ~[int], mut head: uint}; @@ -1286,8 +1299,8 @@ With such a type, you can do `mystack.head += 1u`. If `mut` were omitted from the type, such an assignment would result in a type error. -To 'update' an immutable record, you use functional record update -syntax, by ending a record literal with the keyword `with`: +To create a new record based on the value of an existing record +you construct it using the `with` keyword: ~~~~ let oldpoint = {x: 10f, y: 20f}; @@ -1295,7 +1308,7 @@ let newpoint = {x: 0f with oldpoint}; assert newpoint == {x: 0f, y: 20f}; ~~~~ -This will create a new struct, copying all the fields from `oldpoint` +This will create a new record, copying all the fields from `oldpoint` into it, except for the ones that are explicitly set in the literal. Rust record types are *structural*. This means that `{x: float, y: @@ -1329,8 +1342,8 @@ the fields of a record, a record pattern may end with `, _` (as in ## Enums -Enums are datatypes that have several different representations. For -example, the type shown earlier: +Enums are datatypes that have several alternate representations. For +example, consider the type shown earlier: ~~~~ # type point = {x: float, y: float}; @@ -1352,7 +1365,7 @@ which can be used to construct values of the type (taking arguments of the specified types). So `circle({x: 0f, y: 0f}, 10f)` is the way to create a new circle. -Enum variants do not have to have parameters. This, for example, is +Enum variants need not have type parameters. This, for example, is equivalent to a C enum: ~~~~ @@ -1448,8 +1461,8 @@ fn point_from_direction(dir: direction) -> point { Tuples in Rust behave exactly like records, except that their fields do not have names (and can thus not be accessed with dot notation). -Tuples can have any arity except for 0 or 1 (though you may see nil, -`()`, as the empty tuple if you like). +Tuples can have any arity except for 0 or 1 (though you may consider +nil, `()`, as the empty tuple if you like). ~~~~ let mytup: (int, int, float) = (10, 20, 30.0); @@ -1472,11 +1485,15 @@ allocating memory and going through a pointer. But for big records, or records with mutable fields, it can be useful to have a single copy on the heap, and refer to that through a pointer. -Rust supports several types of pointers. The simplest is the unsafe -pointer, written `*T`, which is a completely unchecked pointer type -only used in unsafe code (and thus, in typical Rust code, very -rarely). The safe pointer types are `@T` for shared, reference-counted -boxes, and `~T`, for uniquely-owned pointers. +Rust supports several types of pointers. The safe pointer types are +`@T` for shared boxes allocated on the local heap, `~T`, for +uniquely-owned boxes allocated on the exchange heap, and `&T`, for +borrowed pointers, which may point to any memory, and whose lifetimes +are governed by the call stack. + +Rust also has an unsafe pointer, written `*T`, which is a completely +unchecked pointer type only used in unsafe code (and thus, in typical +Rust code, very rarely). All pointer types can be dereferenced with the `*` unary operator. @@ -1496,20 +1513,32 @@ let y = x; // Copy the pointer, increase refcount // When x and y go out of scope, refcount goes to 0, box is freed ~~~~ -***Note:*** We may in the future switch to garbage collection, rather +***Note:*** We will in the future switch to garbage collection, rather than reference counting, for shared boxes. Shared boxes never cross task boundaries. ### Unique boxes -In contrast to shared boxes, unique boxes are not reference counted. -Instead, it is statically guaranteed that only a single owner of the -box exists at any time. +In contrast to shared boxes, unique boxes have a single owner and thus +two unique boxes may not refer to the same memory. All unique boxes +across all tasks are allocated on a single _exchange heap_, where +their uniquely owned nature allows them to be passed between tasks. + +Because unique boxes are uniquely owned, copying them involves allocating +a new unique box and duplicating the contents. Copying unique boxes +is expensive so the compiler will complain if you do. ~~~~ let x = ~10; -let y <- x; +let y = x; // error: copying a non-implicitly copyable type +~~~~ + +If you really want to copy a unique box you must say so explicitly. + +~~~~ +let x = ~10; +let y = copy x; ~~~~ This is where the 'move' (`<-`) operator comes in. It is similar to @@ -1518,6 +1547,11 @@ from `x` to `y`, without violating the constraint that it only has a single owner (if you used assignment instead of the move operator, the box would, in principle, be copied). +~~~~ +let x = ~10; +let y <- x; +~~~~ + Unique boxes, when they do not contain any shared boxes, can be sent to other tasks. The sending task will give up ownership of the box, and won't be able to access it afterwards. The receiving task will From 28fec95c5963bbd524f4271cb7cac63dcd2ec0cf Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 18:25:39 -0700 Subject: [PATCH 13/32] tutorial: Add some work on borrowed pointers --- doc/tutorial.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/tutorial.md b/doc/tutorial.md index 301ca6cf5ccfa..f72557216ad84 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1557,6 +1557,33 @@ to other tasks. The sending task will give up ownership of the box, and won't be able to access it afterwards. The receiving task will become the sole owner of the box. +### Borrowed Pointers + +Rust borrowed pointers are a general purpose reference/pointer type, +similar to the C++ reference type, but guaranteed to point to valid +memory. In contrast to unique pointers, where the holder of a unique +pointer is the owner of the pointed-to memory, borrowed pointers never +imply ownership. Pointers may be borrowed from any type, in which case +the pointer is guaranteed not to outlive the value it points to. + +~~~~ +# fn work_with_foo_by_pointer(f: &str) { } +let foo = "foo"; +work_with_foo_by_pointer(&foo); +~~~~ + +The following shows an example of what is _not_ possible with borrowed +pointers. If you were able to write this then the pointer to `foo` +would outlive `foo` itself. + +~~~~ {.ignore} +let foo_ptr; +{ + let foo = "foo"; + foo_ptr = &foo; +} +~~~~ + ### Mutability All pointer types have a mutable variant, written `@mut T` or `~mut From b5f5676a2ffacf58fc3831846c5357b7e2b46109 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 18:27:42 -0700 Subject: [PATCH 14/32] tutorial: Use consistent casing in headers --- doc/tutorial.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index f72557216ad84..f0bbde068056a 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -11,7 +11,7 @@ comparisons to other languages in the C family. The tutorial covers the whole language, though not with the depth and precision of the [language reference](rust.html). -## Language Overview +## Language overview Rust is a systems programming language with a focus on type safety, memory safety, concurrency and performance. It is intended for writing @@ -38,7 +38,7 @@ high-level features include: * Generics - Functions and types can be parameterized over generic types with optional type constraints -## First Impressions +## First impressions As a curly-brace language in the tradition of C, C++, and JavaScript, Rust looks a lot like other languages you may be familiar with. @@ -1557,7 +1557,7 @@ to other tasks. The sending task will give up ownership of the box, and won't be able to access it afterwards. The receiving task will become the sole owner of the box. -### Borrowed Pointers +### Borrowed pointers Rust borrowed pointers are a general purpose reference/pointer type, similar to the C++ reference type, but guaranteed to point to valid From c4af6e92fbae171c56a4e68666025725555fc9d8 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 6 Jul 2012 19:06:58 -0700 Subject: [PATCH 15/32] rustc: Switch to the new resolution pass --- src/libcore/task.rs | 6 + src/libstd/net_ip.rs | 3 +- src/libsyntax/parse.rs | 6 +- src/libsyntax/parse/comments.rs | 1 + src/libsyntax/parse/parser.rs | 42 +- src/rustc/driver/driver.rs | 23 +- src/rustc/metadata/loader.rs | 1 + src/rustc/middle/resolve3.rs | 450 +++++++++++++----- src/rustc/middle/ty.rs | 7 + src/rustc/middle/typeck.rs | 2 + src/rustdoc/config.rs | 2 + src/test/compile-fail/ambig_impl_2_exe.rs | 4 +- src/test/compile-fail/bad-tag-export-2.rs | 11 - src/test/compile-fail/bad-tag-export-3.rs | 13 - src/test/compile-fail/bad-tag-export-4.rs | 12 - src/test/compile-fail/bad-tag-export.rs | 14 - .../cap-clause-unresolved-copy.rs | 2 +- .../cap-clause-unresolved-move.rs | 2 +- .../class-implements-bad-iface.rs | 4 +- src/test/compile-fail/class-implements-int.rs | 4 +- .../cross-crate-glob-collision.rs | 13 - src/test/compile-fail/export-import.rs | 2 +- src/test/compile-fail/iface-test.rs | 6 +- src/test/compile-fail/import-from-dup.rs | 14 - src/test/compile-fail/import-from-missing.rs | 2 +- src/test/compile-fail/import-glob-circular.rs | 2 +- src/test/compile-fail/import-glob-multiple.rs | 20 - src/test/compile-fail/import-loop-2.rs | 2 +- src/test/compile-fail/import-loop.rs | 2 +- src/test/compile-fail/import.rs | 3 +- src/test/compile-fail/import2.rs | 2 +- src/test/compile-fail/import3.rs | 2 +- src/test/compile-fail/import4.rs | 2 +- src/test/compile-fail/import5.rs | 15 - src/test/compile-fail/issue-1697.rs | 3 +- src/test/compile-fail/not-a-pred.rs | 2 +- src/test/compile-fail/tag-exports-2.rs | 18 - src/test/compile-fail/tag-exports-3.rs | 18 - src/test/compile-fail/tag-exports.rs | 18 - src/test/run-pass/issue2170exe.rs | 2 +- src/test/run-pass/tag-exports.rs | 1 + 41 files changed, 430 insertions(+), 328 deletions(-) delete mode 100644 src/test/compile-fail/bad-tag-export-2.rs delete mode 100644 src/test/compile-fail/bad-tag-export-3.rs delete mode 100644 src/test/compile-fail/bad-tag-export-4.rs delete mode 100644 src/test/compile-fail/bad-tag-export.rs delete mode 100644 src/test/compile-fail/cross-crate-glob-collision.rs delete mode 100644 src/test/compile-fail/import-from-dup.rs delete mode 100644 src/test/compile-fail/import-glob-multiple.rs delete mode 100644 src/test/compile-fail/import5.rs delete mode 100644 src/test/compile-fail/tag-exports-2.rs delete mode 100644 src/test/compile-fail/tag-exports-3.rs delete mode 100644 src/test/compile-fail/tag-exports.rs diff --git a/src/libcore/task.rs b/src/libcore/task.rs index f41e24c623f70..855a090e47453 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -63,6 +63,12 @@ export local_data_get; export local_data_set; export local_data_modify; +export single_threaded; +export thread_per_core; +export thread_per_task; +export manual_threads; +export osmain; + /* Data types */ /// A handle to a task diff --git a/src/libstd/net_ip.rs b/src/libstd/net_ip.rs index 2353a983dc2bd..4585b4564b571 100644 --- a/src/libstd/net_ip.rs +++ b/src/libstd/net_ip.rs @@ -25,6 +25,7 @@ export ip_addr, parse_addr_err; export format_addr; export v4, v6; export get_addr; +export ipv4, ipv6; /// An IP address enum ip_addr { @@ -389,4 +390,4 @@ mod test { let ga_result = get_addr(localhost_name, iotask); assert result::is_err(ga_result); } -} \ No newline at end of file +} diff --git a/src/libsyntax/parse.rs b/src/libsyntax/parse.rs index b2d06311e673a..7a9ec6f32a446 100644 --- a/src/libsyntax/parse.rs +++ b/src/libsyntax/parse.rs @@ -16,10 +16,8 @@ export parse_from_source_str; // unresolved import errors. Maybe resolve3 will fix it. import common::*; import parser::parser; -//import attr::parser_attr; -import attr::*; //resolve bug? -//import common::parser_common; -import common::*; //resolve bug? +import attr::parser_attr; +import common::parser_common; import ast::node_id; import util::interner; // FIXME (#1935): resolve badness diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs index e188331dd2490..bd164402c1d79 100644 --- a/src/libsyntax/parse/comments.rs +++ b/src/libsyntax/parse/comments.rs @@ -9,6 +9,7 @@ export lit; export cmnt_style; export gather_comments_and_literals; export is_doc_comment, doc_comment_style, strip_doc_comment_decoration; +export isolated, trailing, mixed, blank_line; enum cmnt_style { isolated, // No code on either side of each line of the comment diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c94e2acbb2b44..6727ebe4c7753 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -12,7 +12,47 @@ import common::{seq_sep_trailing_disallowed, seq_sep_trailing_allowed, seq_sep_none, token_to_str}; import dvec::{dvec, extensions}; import vec::{push}; -import ast::*; +import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute, + bitand, bitor, bitxor, blk, blk_check_mode, bound_const, + bound_copy, bound_send, bound_trait, box, by_copy, by_move, + by_mutbl_ref, by_ref, by_val, capture_clause, capture_item, + carg_base, carg_ident, cdir_dir_mod, cdir_src_mod, + cdir_view_item, checked_expr, claimed_expr, class_immutable, + class_member, class_method, class_mutable, constr, constr_arg, + constr_general, crate, crate_cfg, crate_directive, decl, + decl_item, decl_local, default_blk, deref, div, expl, expr, + expr_, expr_addr_of, expr_alt, expr_assert, expr_assign, + expr_assign_op, expr_binary, expr_block, expr_break, expr_call, + expr_cast, expr_check, expr_cont, expr_copy, expr_do_body, + expr_fail, expr_field, expr_fn, expr_fn_block, expr_if, + expr_if_check, expr_index, expr_lit, expr_log, expr_loop, + expr_loop_body, expr_mac, expr_move, expr_new, expr_path, + expr_rec, expr_ret, expr_swap, expr_tup, expr_unary, expr_vec, + expr_vstore, expr_while, extern_fn, field, fn_decl, foreign_item, + foreign_item_fn, foreign_mod, ident, impure_fn, infer, + init_assign, init_move, initializer, instance_var, item, item_, + item_class, item_const, item_enum, item_fn, item_foreign_mod, + item_impl, item_mod, item_trait, item_ty, lit, lit_, lit_bool, + lit_float, lit_int, lit_int_unsuffixed, lit_nil, lit_str, + lit_uint, local, m_const, m_imm, m_mutbl, mac_, mac_aq, + mac_ellipsis, mac_embed_block, mac_embed_type, mac_invoc, + mac_invoc_tt, mac_var, matcher, method, mode, mt, mtc_bb, + mtc_rep, mtc_tok, mul, mutability, neg, noreturn, not, pat, + pat_box, pat_enum, pat_ident, pat_lit, pat_range, pat_rec, + pat_tup, pat_uniq, pat_wild, path, private, proto, proto_any, + proto_bare, proto_block, proto_box, proto_uniq, public, pure_fn, + purity, re_anon, re_named, region, region_param, rem, ret_style, + return_val, rp_none, rp_self, shl, shr, stmt, stmt_decl, + stmt_expr, stmt_semi, subtract, token_tree, trait_ref, tt_delim, + tt_dotdotdot, tt_flat, tt_interpolate, ty, ty_, ty_bot, ty_box, + ty_constr, ty_constr_, ty_constr_arg, ty_field, ty_fn, ty_infer, + ty_mac, ty_method, ty_nil, ty_param, ty_path, ty_ptr, ty_rec, + ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec, ty_vstore, + unchecked_blk, uniq, unsafe_blk, unsafe_fn, variant, view_item, + view_item_, view_item_export, view_item_import, view_item_use, + view_path, view_path_glob, view_path_list, view_path_simple, + visibility, vstore, vstore_box, vstore_fixed, vstore_slice, + vstore_uniq}; export file_type; export parser; diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs index 799f34377ede4..6c6247fabe3c6 100644 --- a/src/rustc/driver/driver.rs +++ b/src/rustc/driver/driver.rs @@ -168,26 +168,9 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg, session::sess_os_to_meta_os(sess.targ_cfg.os), sess.opts.static)); - let mut def_map; - let mut impl_map; - let mut exp_map; - if sess.fast_resolve() { - let { def_map: fast_dm, exp_map: fast_em, impl_map: fast_im } = - time(time_passes, "fast resolution", || - middle::resolve3::resolve_crate(sess, ast_map, crate)); - - def_map = fast_dm; - impl_map = fast_im; - exp_map = fast_em; - } else { - let { def_map: normal_dm, exp_map: normal_em, impl_map: normal_im } = - time(time_passes, "resolution", || - resolve::resolve_crate(sess, ast_map, crate)); - - def_map = normal_dm; - impl_map = normal_im; - exp_map = normal_em; - } + let { def_map: def_map, exp_map: exp_map, impl_map: impl_map } = + time(time_passes, "fast resolution", || + middle::resolve3::resolve_crate(sess, ast_map, crate)); let freevars = time(time_passes, "freevar finding", || freevars::annotate_freevars(def_map, crate)); diff --git a/src/rustc/metadata/loader.rs b/src/rustc/metadata/loader.rs index 63f3658a4e93e..d92154eb891ec 100644 --- a/src/rustc/metadata/loader.rs +++ b/src/rustc/metadata/loader.rs @@ -9,6 +9,7 @@ import filesearch::filesearch; import io::writer_util; export os; +export os_macos, os_win32, os_linux, os_freebsd; export ctxt; export load_library_crate; export list_file_metadata; diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs index 6ca613d528103..69d3a25d5a156 100644 --- a/src/rustc/middle/resolve3.rs +++ b/src/rustc/middle/resolve3.rs @@ -2,6 +2,7 @@ import driver::session::session; import metadata::csearch::{each_path, get_impls_for_mod, lookup_defs}; import metadata::cstore::find_use_stmt_cnum; import metadata::decoder::{def_like, dl_def, dl_field, dl_impl}; +import middle::lint::{error, ignore, level, unused_imports, warn}; import syntax::ast::{_mod, arm, blk, bound_const, bound_copy, bound_trait}; import syntax::ast::{bound_send, capture_clause, class_ctor, class_dtor}; import syntax::ast::{class_member, class_method, crate, crate_num, decl_item}; @@ -143,7 +144,10 @@ enum TypeParameters/& { // The index at the method site will be 1, because the // outer T had index 0. - uint) + uint, + + // The kind of the rib used for type parameters. + RibKind) } // The rib kind controls the translation of argument or local definitions @@ -152,9 +156,13 @@ enum TypeParameters/& { enum RibKind { // No translation needs to be applied. NormalRibKind, + // We passed through a function scope at the given node ID. Translate // upvars as appropriate. - FunctionRibKind(node_id) + FunctionRibKind(node_id), + + // We passed through a function *item* scope. Disallow upvars. + OpaqueFunctionRibKind } // The X-ray flag indicates that a context has the X-ray privilege, which @@ -169,6 +177,17 @@ enum XrayFlag { Xray //< Private items can be accessed. } +enum AllowCapturingSelfFlag { + AllowCapturingSelf, //< The "self" definition can be captured. + DontAllowCapturingSelf, //< The "self" definition cannot be captured. +} + +enum EnumVariantOrConstResolution { + FoundEnumVariant(def), + FoundConst, + EnumVariantOrConstNotFound +} + // FIXME (issue #2550): Should be a class but then it becomes not implicitly // copyable due to a kind bug. @@ -258,10 +277,15 @@ class Rib { class ImportDirective { let module_path: @dvec; let subclass: @ImportDirectiveSubclass; + let span: span; + + new(module_path: @dvec, + subclass: @ImportDirectiveSubclass, + span: span) { - new(module_path: @dvec, subclass: @ImportDirectiveSubclass) { self.module_path = module_path; self.subclass = subclass; + self.span = span; } } @@ -277,6 +301,8 @@ class Target { } class ImportResolution { + let span: span; + // The number of outstanding references to this name. When this reaches // zero, outside modules can count on the targets being correct. Before // then, all bets are off; future imports could override this name. @@ -288,13 +314,19 @@ class ImportResolution { let mut type_target: option; let mut impl_target: @dvec<@Target>; - new() { + let mut used: bool; + + new(span: span) { + self.span = span; + self.outstanding_references = 0u; self.module_target = none; self.value_target = none; self.type_target = none; self.impl_target = @dvec(); + + self.used = false; } fn target_for_namespace(namespace: Namespace) -> option { @@ -398,10 +430,18 @@ pure fn is_none(x: option) -> bool { } } -/** - * Records the definitions (at most one for each namespace) that a name is - * bound to. - */ +fn unused_import_lint_level(session: session) -> level { + for session.opts.lint_opts.each |lint_option_pair| { + let (lint_type, lint_level) = lint_option_pair; + if lint_type == unused_imports { + ret lint_level; + } + } + ret ignore; +} + +// Records the definitions (at most one for each namespace) that a name is +// bound to. class NameBindings { let mut module_def: ModuleDef; //< Meaning in the module namespace. let mut type_def: option; //< Meaning in the type namespace. @@ -549,6 +589,8 @@ class Resolver { let graph_root: @NameBindings; + let unused_import_lint_level: level; + // The number of imports that are currently unresolved. let mut unresolved_imports: uint; @@ -594,6 +636,8 @@ class Resolver { (*self.graph_root).define_module(NoParentLink, some({ crate: 0, node: 0 })); + self.unused_import_lint_level = unused_import_lint_level(session); + self.unresolved_imports = 0u; self.current_module = (*self.graph_root).get_module(); @@ -614,10 +658,21 @@ class Resolver { /// The main name resolution procedure. fn resolve(this: @Resolver) { self.build_reduced_graph(this); + self.session.abort_if_errors(); + self.resolve_imports(); + self.session.abort_if_errors(); + self.record_exports(); + self.session.abort_if_errors(); + self.build_impl_scopes(); + self.session.abort_if_errors(); + self.resolve_crate(); + self.session.abort_if_errors(); + + self.check_for_unused_imports_if_necessary(); } // @@ -945,7 +1000,8 @@ class Resolver { source_atom); self.build_import_directive(module, module_path, - subclass); + subclass, + view_path.span); } view_path_list(_, source_idents, _) { for source_idents.each |source_ident| { @@ -954,13 +1010,15 @@ class Resolver { let subclass = @SingleImport(atom, atom); self.build_import_directive(module, module_path, - subclass); + subclass, + view_path.span); } } view_path_glob(_, _) { self.build_import_directive(module, module_path, - @GlobImport); + @GlobImport, + view_path.span); } } } @@ -1066,7 +1124,8 @@ class Resolver { do self.with_type_parameter_rib (HasTypeParameters(&type_parameters, foreign_item.id, - 0u)) || { + 0u, + NormalRibKind)) || { visit_foreign_item(foreign_item, new_parent, visitor); } @@ -1292,9 +1351,10 @@ class Resolver { /// Creates and adds an import directive to the given module. fn build_import_directive(module: @Module, module_path: @dvec, - subclass: @ImportDirectiveSubclass) { + subclass: @ImportDirectiveSubclass, + span: span) { - let directive = @ImportDirective(module_path, subclass); + let directive = @ImportDirective(module_path, subclass, span); module.imports.push(directive); // Bump the reference count on the name. Or, if this is a glob, set @@ -1307,7 +1367,7 @@ class Resolver { resolution.outstanding_references += 1u; } none { - let resolution = @ImportResolution(); + let resolution = @ImportResolution(span); resolution.outstanding_references = 1u; module.import_resolutions.insert(target, resolution); } @@ -1403,9 +1463,8 @@ class Resolver { alt self.resolve_import_for_module(module, import_directive) { Failed { // We presumably emitted an error. Continue. - // XXX: span_err - self.session.err(#fmt("failed to resolve import in: %s", - self.module_to_str(module))); + self.session.span_err(import_directive.span, + "failed to resolve import"); } Indeterminate { // Bail out. We'll come around next time. @@ -1450,7 +1509,8 @@ class Resolver { // First, resolve the module path for the directive, if necessary. alt self.resolve_module_path_for_import(module, module_path, - NoXray) { + NoXray, + import_directive.span) { Failed { resolution_result = Failed; @@ -1471,9 +1531,11 @@ class Resolver { source); } GlobImport { + let span = import_directive.span; resolution_result = self.resolve_glob_import(module, - containing_module); + containing_module, + span); } } } @@ -1610,6 +1672,7 @@ class Resolver { some(import_resolution) if import_resolution.outstanding_references == 0u { + fn get_binding(import_resolution: @ImportResolution, namespace: Namespace) -> NamespaceResult { @@ -1620,6 +1683,7 @@ class Resolver { ret UnboundResult; } some(target) { + import_resolution.used = true; ret BoundResult(target.target_module, target.bindings); } @@ -1730,7 +1794,9 @@ class Resolver { * succeeds or bails out (as importing * from an empty module or a module * that exports nothing is valid). */ - fn resolve_glob_import(module: @Module, containing_module: @Module) + fn resolve_glob_import(module: @Module, + containing_module: @Module, + span: span) -> ResolveResult<()> { // This function works in a highly imperative manner; it eagerly adds @@ -1767,7 +1833,8 @@ class Resolver { alt module.import_resolutions.find(atom) { none { // Simple: just copy the old import resolution. - let new_import_resolution = @ImportResolution(); + let new_import_resolution = + @ImportResolution(target_import_resolution.span); new_import_resolution.module_target = copy target_import_resolution.module_target; new_import_resolution.value_target = @@ -1828,12 +1895,17 @@ class Resolver { // Add all children from the containing module. for containing_module.children.each |atom, name_bindings| { + if !self.name_is_exported(containing_module, atom) { + #debug("(resolving glob import) name '%s' is unexported", + *(*self.atom_table).atom_to_str(atom)); + cont; + } let mut dest_import_resolution; alt module.import_resolutions.find(atom) { none { // Create a new import resolution from this child. - dest_import_resolution = @ImportResolution(); + dest_import_resolution = @ImportResolution(span); module.import_resolutions.insert (atom, dest_import_resolution); } @@ -1879,7 +1951,8 @@ class Resolver { fn resolve_module_path_from_root(module: @Module, module_path: @dvec, index: uint, - xray: XrayFlag) + xray: XrayFlag, + span: span) -> ResolveResult<@Module> { let mut search_module = module; @@ -1896,10 +1969,7 @@ class Resolver { xray) { Failed { - // XXX: span_err - self.session.err(#fmt("module resolution failed: %s", - *(*self.atom_table) - .atom_to_str(name))); + self.session.span_err(span, "unresolved name"); ret Failed; } Indeterminate { @@ -1912,10 +1982,10 @@ class Resolver { alt target.bindings.module_def { NoModuleDef { // Not a module. - // XXX: span_err - self.session.err(#fmt("not a module: %s", - *(*self.atom_table). - atom_to_str(name))); + self.session.span_err(span, + #fmt("not a module: %s", + *(*self.atom_table). + atom_to_str(name))); ret Failed; } ModuleDef(module) { @@ -1937,7 +2007,8 @@ class Resolver { */ fn resolve_module_path_for_import(module: @Module, module_path: @dvec, - xray: XrayFlag) + xray: XrayFlag, + span: span) -> ResolveResult<@Module> { let module_path_len = (*module_path).len(); @@ -1955,10 +2026,7 @@ class Resolver { let mut search_module; alt self.resolve_module_in_lexical_scope(module, first_element) { Failed { - // XXX: span_err - self.session.err(#fmt("unresolved name: %s", - *(*self.atom_table). - atom_to_str(first_element))); + self.session.span_err(span, "unresolved name"); ret Failed; } Indeterminate { @@ -1974,7 +2042,8 @@ class Resolver { ret self.resolve_module_path_from_root(search_module, module_path, 1u, - xray); + xray, + span); } fn resolve_item_in_lexical_scope(module: @Module, @@ -2018,6 +2087,7 @@ class Resolver { namespace); } some(target) { + import_resolution.used = true; ret Success(copy target); } } @@ -2157,6 +2227,7 @@ class Resolver { some(target) { #debug("(resolving name in module) resolved to \ import"); + import_resolution.used = true; ret Success(copy target); } } @@ -2316,8 +2387,7 @@ class Resolver { if is_none(module_result) && is_none(value_result) && is_none(type_result) && is_none(impl_result) { - // XXX: span_err, better error - self.session.err("couldn't find anything with that name"); + self.session.span_err(import_directive.span, "unresolved import"); ret Failed; } @@ -2361,13 +2431,8 @@ class Resolver { let index = module.resolved_import_count; let import_count = module.imports.len(); if index != import_count { - let module_path = module.imports.get_elt(index).module_path; - - // XXX: span_err - self.session.err(#fmt("unresolved import in %s: %s", - self.module_to_str(module), - *(*self.atom_table) - .atoms_to_str((*module_path).get()))); + self.session.span_err(module.imports.get_elt(index).span, + "unresolved import"); } // Descend into children and anonymous children. @@ -2452,7 +2517,8 @@ class Resolver { alt self.resolve_definition_of_name_in_module(module, name, - namespace) { + namespace, + Xray) { NoNameDefinition { // Nothing to do. } @@ -2511,7 +2577,7 @@ class Resolver { for module.children.each |_atom, child_name_bindings| { alt (*child_name_bindings).get_module_if_available() { none { - /* Nothing to do. */ + // Nothing to do. } some(child_module) { self.build_impl_scopes_for_module_subtree(child_module); @@ -2577,10 +2643,7 @@ class Resolver { // AST resolution // - // We maintain a list of value ribs and type ribs. Since ribs are - // somewhat expensive to allocate, we try to avoid creating ribs unless - // we know we need to. For instance, we don't allocate a type rib for - // a function with no type parameters. + // We maintain a list of value ribs and type ribs. // // Simultaneously, we keep track of the current position in the module // graph in the `current_module` pointer. When we go to resolve a name in @@ -2634,18 +2697,30 @@ class Resolver { // Wraps the given definition in the appropriate number of `def_upvar` // wrappers. - fn upvarify(ribs: @dvec<@Rib>, rib_index: uint, def_like: def_like) - -> def_like { + fn upvarify(ribs: @dvec<@Rib>, rib_index: uint, def_like: def_like, + span: span, allow_capturing_self: AllowCapturingSelfFlag) + -> option { let mut def; + let mut is_ty_param; + alt def_like { dl_def(d @ def_local(*)) | dl_def(d @ def_upvar(*)) | - dl_def(d @ def_arg(*)) | dl_def(d @ def_self(*)) | - dl_def(d @ def_binding(*)) { + dl_def(d @ def_arg(*)) | dl_def(d @ def_binding(*)) { + def = d; + is_ty_param = false; + } + dl_def(d @ def_ty_param(*)) { def = d; + is_ty_param = true; + } + dl_def(d @ def_self(*)) + if allow_capturing_self == DontAllowCapturingSelf { + def = d; + is_ty_param = false; } _ { - ret def_like; + ret some(def_like); } } @@ -2657,19 +2732,43 @@ class Resolver { // Nothing to do. Continue. } FunctionRibKind(function_id) { - def = def_upvar(def_id_of_def(def).node, - @def, - function_id); + if !is_ty_param { + def = def_upvar(def_id_of_def(def).node, + @def, + function_id); + } + } + OpaqueFunctionRibKind { + if !is_ty_param { + // This was an attempt to access an upvar inside a + // named function item. This is not allowed, so we + // report an error. + + self.session.span_err(span, + "attempted dynamic environment-\ + capture"); + } else { + // This was an attempt to use a type parameter outside + // its scope. + + self.session.span_err(span, + "attempt to use a type \ + argument out of scope"); + } + + ret none; } } rib_index += 1u; } - ret dl_def(def); + ret some(dl_def(def)); } - fn search_ribs(ribs: @dvec<@Rib>, name: Atom) -> option { + fn search_ribs(ribs: @dvec<@Rib>, name: Atom, span: span, + allow_capturing_self: AllowCapturingSelfFlag) + -> option { // XXX: This should not use a while loop. // XXX: Try caching? @@ -2680,7 +2779,8 @@ class Resolver { let rib = (*ribs).get_elt(i); alt rib.bindings.find(name) { some(def_like) { - ret some(self.upvarify(ribs, i, def_like)); + ret self.upvarify(ribs, i, def_like, span, + allow_capturing_self); } none { // Continue. @@ -2733,7 +2833,8 @@ class Resolver { item_enum(_, type_parameters, _) | item_ty(_, type_parameters, _) { do self.with_type_parameter_rib - (HasTypeParameters(&type_parameters, item.id, 0u)) + (HasTypeParameters(&type_parameters, item.id, 0u, + NormalRibKind)) || { visit_item(item, (), visitor); @@ -2760,7 +2861,8 @@ class Resolver { // Create a new rib for the interface-wide type parameters. do self.with_type_parameter_rib - (HasTypeParameters(&type_parameters, item.id, 0u)) + (HasTypeParameters(&type_parameters, item.id, 0u, + NormalRibKind)) || { for methods.each |method| { @@ -2772,7 +2874,8 @@ class Resolver { do self.with_type_parameter_rib (HasTypeParameters(&method.tps, item.id, - type_parameters.len())) + type_parameters.len(), + NormalRibKind)) || { // Resolve the method-specific type parameters. @@ -2819,7 +2922,9 @@ class Resolver { do self.with_type_parameter_rib (HasTypeParameters(&type_parameters, foreign_item.id, - 0u)) || { + 0u, + OpaqueFunctionRibKind)) + || { visit_foreign_item(foreign_item, (), visitor); @@ -2844,11 +2949,13 @@ class Resolver { self.session.main_fn = some((item.id, item.span)); } - self.resolve_function(NormalRibKind, + self.resolve_function(OpaqueFunctionRibKind, some(@fn_decl), - HasTypeParameters(&ty_params, - item.id, - 0u), + HasTypeParameters + (&ty_params, + item.id, + 0u, + OpaqueFunctionRibKind), block, NoSelfBinding, NoCaptureClause, @@ -2869,10 +2976,10 @@ class Resolver { fn with_type_parameter_rib(type_parameters: TypeParameters, f: fn()) { alt type_parameters { - HasTypeParameters(type_parameters, node_id, initial_index) - if (*type_parameters).len() >= 1u { + HasTypeParameters(type_parameters, node_id, initial_index, + rib_kind) { - let function_type_rib = @Rib(NormalRibKind); + let function_type_rib = @Rib(rib_kind); (*self.type_ribs).push(function_type_rib); for (*type_parameters).eachi |index, type_parameter| { @@ -2885,7 +2992,7 @@ class Resolver { } } - HasTypeParameters(*) | NoTypeParameters { + NoTypeParameters { // Nothing to do. } } @@ -2893,13 +3000,11 @@ class Resolver { f(); alt type_parameters { - HasTypeParameters(type_parameters, _, _) - if (*type_parameters).len() >= 1u { - + HasTypeParameters(type_parameters, _, _, _) { (*self.type_ribs).pop(); } - HasTypeParameters(*) | NoTypeParameters { + NoTypeParameters { // Nothing to do. } } @@ -2923,11 +3028,11 @@ class Resolver { for (*capture_clause).each |capture_item| { alt self.resolve_identifier(capture_item.name, ValueNS, - true) { + true, + capture_item.span) { none { self.session.span_err(capture_item.span, - "use of undeclared \ - identifier in \ + "unresolved name in \ capture clause"); } some(def) { @@ -2949,7 +3054,7 @@ class Resolver { NoTypeParameters { // Continue. } - HasTypeParameters(type_parameters, _, _) { + HasTypeParameters(type_parameters, _, _, _) { self.resolve_type_parameters(*type_parameters, visitor); } } @@ -2992,8 +3097,8 @@ class Resolver { false, visitor) { none { self.session.span_err(constraint.span, - "use of undeclared \ - constraint"); + #fmt("unresolved name: %s", + *constraint.node.path.idents.last())); } some(def) { self.record_def(constraint.node.id, def); @@ -3044,7 +3149,8 @@ class Resolver { let outer_type_parameter_count = (*type_parameters).len(); let borrowed_type_parameters: &~[ty_param] = &*type_parameters; do self.with_type_parameter_rib(HasTypeParameters - (borrowed_type_parameters, id, 0u)) + (borrowed_type_parameters, id, 0u, + NormalRibKind)) || { // Resolve the type parameters. @@ -3055,8 +3161,8 @@ class Resolver { alt self.resolve_path(interface.path, TypeNS, true, visitor) { none { self.session.span_err(interface.path.span, - "attempt to implement an \ - unknown interface"); + "attempt to implement a \ + nonexistent interface"); } some(def) { // Write a mapping from the interface ID to the @@ -3083,7 +3189,8 @@ class Resolver { let type_parameters = HasTypeParameters(borrowed_method_type_parameters, method.id, - outer_type_parameter_count); + outer_type_parameter_count, + NormalRibKind); self.resolve_function(NormalRibKind, some(@method.decl), type_parameters, @@ -3139,7 +3246,8 @@ class Resolver { let outer_type_parameter_count = type_parameters.len(); let borrowed_type_parameters: &~[ty_param] = &type_parameters; do self.with_type_parameter_rib(HasTypeParameters - (borrowed_type_parameters, id, 0u)) + (borrowed_type_parameters, id, 0u, + NormalRibKind)) || { // Resolve the type parameters. @@ -3178,7 +3286,8 @@ class Resolver { HasTypeParameters (borrowed_type_parameters, method.id, - outer_type_parameter_count), + outer_type_parameter_count, + NormalRibKind), method.body, HasSelfBinding(method.self_id), NoCaptureClause, @@ -3373,19 +3482,32 @@ class Resolver { // such a variant is simply disallowed (since it's rarely // what you want). - // XXX: This restriction is not yet implemented. - let atom = (*self.atom_table).intern(path.idents[0]); - alt self.resolve_enum_variant(atom) { - some(def) { + alt self.resolve_enum_variant_or_const(atom) { + FoundEnumVariant(def) if mode == RefutableMode { #debug("(resolving pattern) resolving '%s' to \ enum variant", *path.idents[0]); self.record_def(pattern.id, def); } - none { + FoundEnumVariant(_) { + self.session.span_err(pattern.span, + #fmt("declaration of `%s` \ + shadows an enum \ + that's in scope", + *(*self.atom_table). + atom_to_str + (atom))); + } + FoundConst { + self.session.span_err(pattern.span, + "pattern variable \ + conflicts with a constant \ + in scope"); + } + EnumVariantOrConstNotFound { #debug("(resolving pattern) binding '%s'", *path.idents[0]); @@ -3457,7 +3579,7 @@ class Resolver { } none { self.session.span_err(path.span, - "undeclared enum variant"); + "unresolved enum variant"); } } @@ -3474,7 +3596,9 @@ class Resolver { } } - fn resolve_enum_variant(name: Atom) -> option { + fn resolve_enum_variant_or_const(name: Atom) + -> EnumVariantOrConstResolution { + alt self.resolve_item_in_lexical_scope(self.current_module, name, ValueNS) { @@ -3486,10 +3610,13 @@ class Resolver { of name bindings with no def?!"; } some(def @ def_variant(*)) { - ret some(def); + ret FoundEnumVariant(def); + } + some(def_const(*)) { + ret FoundConst; } some(_) { - ret none; + ret EnumVariantOrConstNotFound; } } } @@ -3499,7 +3626,7 @@ class Resolver { } Failed { - ret none; + ret EnumVariantOrConstNotFound; } } } @@ -3531,16 +3658,20 @@ class Resolver { ret self.resolve_identifier(path.idents.last(), namespace, - check_ribs); + check_ribs, + path.span); } fn resolve_identifier(identifier: ident, namespace: Namespace, - check_ribs: bool) + check_ribs: bool, + span: span) -> option { if check_ribs { - alt self.resolve_identifier_in_local_ribs(identifier, namespace) { + alt self.resolve_identifier_in_local_ribs(identifier, + namespace, + span) { some(def) { ret some(def); } @@ -3557,9 +3688,17 @@ class Resolver { // XXX: Merge me with resolve_name_in_module? fn resolve_definition_of_name_in_module(containing_module: @Module, name: Atom, - namespace: Namespace) + namespace: Namespace, + xray: XrayFlag) -> NameDefinition { + if xray == NoXray && !self.name_is_exported(containing_module, name) { + #debug("(resolving definition of name in module) name '%s' is \ + unexported", + *(*self.atom_table).atom_to_str(name)); + ret NoNameDefinition; + } + // First, search children. alt containing_module.children.find(name) { some(child_name_bindings) { @@ -3586,6 +3725,7 @@ class Resolver { alt (*target.bindings).def_for_namespace(namespace) { some(def) { // Found it. + import_resolution.used = true; ret ImportNameDefinition(def); } none { @@ -3629,7 +3769,8 @@ class Resolver { let mut containing_module; alt self.resolve_module_path_for_import(self.current_module, module_path_atoms, - xray) { + xray, + path.span) { Failed { self.session.span_err(path.span, @@ -3651,7 +3792,8 @@ class Resolver { let name = (*self.atom_table).intern(path.idents.last()); alt self.resolve_definition_of_name_in_module(containing_module, name, - namespace) { + namespace, + xray) { NoNameDefinition { // We failed to resolve the name. Report an error. self.session.span_err(path.span, @@ -3681,7 +3823,8 @@ class Resolver { alt self.resolve_module_path_from_root(root_module, module_path_atoms, 0u, - xray) { + xray, + path.span) { Failed { self.session.span_err(path.span, @@ -3703,7 +3846,8 @@ class Resolver { let name = (*self.atom_table).intern(path.idents.last()); alt self.resolve_definition_of_name_in_module(containing_module, name, - namespace) { + namespace, + xray) { NoNameDefinition { // We failed to resolve the name. Report an error. self.session.span_err(path.span, @@ -3721,7 +3865,8 @@ class Resolver { } fn resolve_identifier_in_local_ribs(identifier: ident, - namespace: Namespace) + namespace: Namespace, + span: span) -> option { let name = (*self.atom_table).intern(identifier); @@ -3730,10 +3875,12 @@ class Resolver { let mut search_result; alt namespace { ValueNS { - search_result = self.search_ribs(self.value_ribs, name); + search_result = self.search_ribs(self.value_ribs, name, span, + DontAllowCapturingSelf); } TypeNS { - search_result = self.search_ribs(self.type_ribs, name); + search_result = self.search_ribs(self.type_ribs, name, span, + AllowCapturingSelf); } ModuleNS | ImplNS { fail "module or impl namespaces do not have local ribs"; @@ -3860,6 +4007,83 @@ class Resolver { self.def_map.insert(node_id, def); } + // + // Unused import checking + // + // Although this is a lint pass, it lives in here because it depends on + // resolve data structures. + // + + fn check_for_unused_imports_if_necessary() { + if self.unused_import_lint_level == ignore { + ret; + } + + let root_module = (*self.graph_root).get_module(); + self.check_for_unused_imports_in_module_subtree(root_module); + } + + fn check_for_unused_imports_in_module_subtree(module: @Module) { + // If this isn't a local crate, then bail out. We don't need to check + // for unused imports in external crates. + + alt module.def_id { + some(def_id) if def_id.crate == local_crate { + // OK. Continue. + } + none { + // Check for unused imports in the root module. + } + some(_) { + // Bail out. + #debug("(checking for unused imports in module subtree) not \ + checking for unused imports for '%s'", + self.module_to_str(module)); + ret; + } + } + + self.check_for_unused_imports_in_module(module); + + for module.children.each |_atom, child_name_bindings| { + alt (*child_name_bindings).get_module_if_available() { + none { + // Nothing to do. + } + some(child_module) { + self.check_for_unused_imports_in_module_subtree + (child_module); + } + } + } + + for module.anonymous_children.each |_node_id, child_module| { + self.check_for_unused_imports_in_module_subtree(child_module); + } + } + + fn check_for_unused_imports_in_module(module: @Module) { + for module.import_resolutions.each |_impl_name, import_resolution| { + if !import_resolution.used { + alt self.unused_import_lint_level { + warn { + self.session.span_warn(import_resolution.span, + "unused import"); + } + error { + self.session.span_err(import_resolution.span, + "unused import"); + } + ignore { + self.session.span_bug(import_resolution.span, + "shouldn't be here if lint \ + pass is ignored"); + } + } + } + } + } + // // Diagnostics // diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index fba8d54cfba01..56e0c39c56626 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -166,6 +166,13 @@ export ty_sort_str; export normalize_ty; export to_str; export borrow, serialize_borrow, deserialize_borrow; +export bound_const; +export terr_no_integral_type, terr_ty_param_size, terr_self_substs; +export terr_in_field, terr_record_fields, terr_vstores_differ, terr_arg_count; +export terr_sorts, terr_vec, terr_str, terr_record_size, terr_tuple_size; +export terr_regions_differ, terr_mutability, terr_purity_mismatch; +export terr_constr_mismatch, terr_constr_len, terr_proto_mismatch; +export terr_ret_style_mismatch; // Data types diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index aaad7841d4c0c..1e198f0f82e2d 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -75,6 +75,8 @@ export deserialize_method_map_entry; export vtable_map; export vtable_res; export vtable_origin; +export method_static, method_param, method_trait; +export vtable_static, vtable_param, vtable_trait; #[auto_serialize] enum method_origin { diff --git a/src/rustdoc/config.rs b/src/rustdoc/config.rs index 94d232778679b..ea397a4398ea5 100644 --- a/src/rustdoc/config.rs +++ b/src/rustdoc/config.rs @@ -7,6 +7,8 @@ export config; export default_config; export parse_config; export usage; +export markdown, pandoc_html; +export doc_per_crate, doc_per_mod; /// The type of document to output enum output_format { diff --git a/src/test/compile-fail/ambig_impl_2_exe.rs b/src/test/compile-fail/ambig_impl_2_exe.rs index 7cb79ff789e12..fe7e27dce2316 100644 --- a/src/test/compile-fail/ambig_impl_2_exe.rs +++ b/src/test/compile-fail/ambig_impl_2_exe.rs @@ -2,6 +2,6 @@ // aux-build:ambig_impl_2_lib.rs use ambig_impl_2_lib; import ambig_impl_2_lib::methods1; -impl methods2 for uint { fn me() -> uint { self } } //~ NOTE candidate #2 is `methods2::me` +impl methods2 for uint { fn me() -> uint { self } } //~ NOTE is `methods2::me` fn main() { 1u.me(); } //~ ERROR multiple applicable methods in scope -//~^ NOTE candidate #1 is `ambig_impl_2_lib::methods1::me` +//~^ NOTE is `ambig_impl_2_lib::methods1::me` diff --git a/src/test/compile-fail/bad-tag-export-2.rs b/src/test/compile-fail/bad-tag-export-2.rs deleted file mode 100644 index 09018c2167f9a..0000000000000 --- a/src/test/compile-fail/bad-tag-export-2.rs +++ /dev/null @@ -1,11 +0,0 @@ -// error-pattern:b does not refer to an enumeration -import bad::*; - -mod bad { - export b::{}; - - fn b() { fail; } -} - -fn main() { -} \ No newline at end of file diff --git a/src/test/compile-fail/bad-tag-export-3.rs b/src/test/compile-fail/bad-tag-export-3.rs deleted file mode 100644 index e6934688e21b9..0000000000000 --- a/src/test/compile-fail/bad-tag-export-3.rs +++ /dev/null @@ -1,13 +0,0 @@ -// error-pattern:b does not refer to an enumeration -import bad::*; - -mod bad { - export b::{f, z}; - - fn b() { fail; } - fn f() { fail; } - fn z() { fail; } -} - -fn main() { -} \ No newline at end of file diff --git a/src/test/compile-fail/bad-tag-export-4.rs b/src/test/compile-fail/bad-tag-export-4.rs deleted file mode 100644 index fadf0c353a36a..0000000000000 --- a/src/test/compile-fail/bad-tag-export-4.rs +++ /dev/null @@ -1,12 +0,0 @@ -// error-pattern:f is not a variant -import bad::*; - -mod bad { - export b::{f, z}; - - enum b { z, k } - fn f() { fail; } -} - -fn main() { -} \ No newline at end of file diff --git a/src/test/compile-fail/bad-tag-export.rs b/src/test/compile-fail/bad-tag-export.rs deleted file mode 100644 index fb9d9b8682c6c..0000000000000 --- a/src/test/compile-fail/bad-tag-export.rs +++ /dev/null @@ -1,14 +0,0 @@ -// error-pattern:variant e doesn't belong to enum floop -import bad::*; - -mod bad { - - export floop::{a, e}; - - enum floop {a, b, c} - enum bloop {d, e, f} - -} - -fn main() { -} \ No newline at end of file diff --git a/src/test/compile-fail/cap-clause-unresolved-copy.rs b/src/test/compile-fail/cap-clause-unresolved-copy.rs index 97655cb3bd760..c1ddaff9a9505 100644 --- a/src/test/compile-fail/cap-clause-unresolved-copy.rs +++ b/src/test/compile-fail/cap-clause-unresolved-copy.rs @@ -1,4 +1,4 @@ -// error-pattern:unresolved name: z +// error-pattern:unresolved name fn main() { let x = 5; let y = fn~(copy z, copy x) { diff --git a/src/test/compile-fail/cap-clause-unresolved-move.rs b/src/test/compile-fail/cap-clause-unresolved-move.rs index 292b0f430541a..5056c2795910c 100644 --- a/src/test/compile-fail/cap-clause-unresolved-move.rs +++ b/src/test/compile-fail/cap-clause-unresolved-move.rs @@ -1,4 +1,4 @@ -// error-pattern:unresolved name: z +// error-pattern:unresolved name fn main() { let x = 5; let y = fn~(move z, move x) { diff --git a/src/test/compile-fail/class-implements-bad-iface.rs b/src/test/compile-fail/class-implements-bad-iface.rs index 4e95b986a9ab8..997a99a94a23d 100644 --- a/src/test/compile-fail/class-implements-bad-iface.rs +++ b/src/test/compile-fail/class-implements-bad-iface.rs @@ -1,4 +1,4 @@ -// error-pattern:unresolved typename: nonexistent +// error-pattern:nonexistent class cat : nonexistent { let meows: uint; new(in_x : uint) { self.meows = in_x; } @@ -6,4 +6,4 @@ class cat : nonexistent { fn main() { let nyan = cat(0u); -} \ No newline at end of file +} diff --git a/src/test/compile-fail/class-implements-int.rs b/src/test/compile-fail/class-implements-int.rs index 469fee7863b97..513b539a45e35 100644 --- a/src/test/compile-fail/class-implements-int.rs +++ b/src/test/compile-fail/class-implements-int.rs @@ -1,8 +1,8 @@ -class cat : int { //~ ERROR can only implement interface types +class cat : int { //~ ERROR interface let meows: uint; new(in_x : uint) { self.meows = in_x; } } fn main() { let nyan = cat(0u); -} \ No newline at end of file +} diff --git a/src/test/compile-fail/cross-crate-glob-collision.rs b/src/test/compile-fail/cross-crate-glob-collision.rs deleted file mode 100644 index c9694739b493c..0000000000000 --- a/src/test/compile-fail/cross-crate-glob-collision.rs +++ /dev/null @@ -1,13 +0,0 @@ -// error-pattern: is glob-imported from multiple different modules -// issue #482 - -use std; -// expecting swap to be defined in vec -import vec::*; -import alternate_supplier::*; - -mod alternate_supplier { - fn contains() { } -} - -fn main() { contains() } diff --git a/src/test/compile-fail/export-import.rs b/src/test/compile-fail/export-import.rs index f5ed4c4f3a788..f010abd6a16e3 100644 --- a/src/test/compile-fail/export-import.rs +++ b/src/test/compile-fail/export-import.rs @@ -1,4 +1,4 @@ -// error-pattern: unresolved import +// error-pattern: import import m::unexported; diff --git a/src/test/compile-fail/iface-test.rs b/src/test/compile-fail/iface-test.rs index 1d45832d6df1c..bd50cb8d06ccb 100644 --- a/src/test/compile-fail/iface-test.rs +++ b/src/test/compile-fail/iface-test.rs @@ -1,9 +1,9 @@ iface foo { fn foo(); } -impl of foo for uint {} //~ ERROR missing method `foo` +impl of foo for uint {} -impl of foo for uint { fn foo() -> int {} } //~ ERROR incompatible type +impl of foo for uint { fn foo() -> int {} } -impl of int for uint { fn foo() {} } //~ ERROR can only implement interface +impl of int for uint { fn foo() {} } //~ ERROR interface fn main() {} diff --git a/src/test/compile-fail/import-from-dup.rs b/src/test/compile-fail/import-from-dup.rs deleted file mode 100644 index 73e14bfc753f6..0000000000000 --- a/src/test/compile-fail/import-from-dup.rs +++ /dev/null @@ -1,14 +0,0 @@ -// error-pattern:duplicate definition of f - -import m1::{f}; -import m2::{f}; - -mod m1 { - fn f() { } -} - -mod m2 { - fn f() { } -} - -fn main() { } diff --git a/src/test/compile-fail/import-from-missing.rs b/src/test/compile-fail/import-from-missing.rs index 87434a6c5f362..a22c1ede9abcf 100644 --- a/src/test/compile-fail/import-from-missing.rs +++ b/src/test/compile-fail/import-from-missing.rs @@ -1,4 +1,4 @@ -// error-pattern:unresolved import +// error-pattern:unresolved import spam::{ham, eggs}; mod spam { diff --git a/src/test/compile-fail/import-glob-circular.rs b/src/test/compile-fail/import-glob-circular.rs index 588b0d5848ee5..66be16d28a4ec 100644 --- a/src/test/compile-fail/import-glob-circular.rs +++ b/src/test/compile-fail/import-glob-circular.rs @@ -1,5 +1,5 @@ +// error-pattern: unresolved -// error-pattern: unresolved name mod circ1 { import circ1::*; export f1; diff --git a/src/test/compile-fail/import-glob-multiple.rs b/src/test/compile-fail/import-glob-multiple.rs deleted file mode 100644 index 83672579130fe..0000000000000 --- a/src/test/compile-fail/import-glob-multiple.rs +++ /dev/null @@ -1,20 +0,0 @@ -// error-pattern:common2 - -import mod1::*; -import mod2::*; - -mod mod1 { - fn f1() { #debug("f1"); } - fn common1() { #debug("common") } - fn common2() { #debug("common") } -} - -mod mod2 { - fn f2() { #debug("f1"); } - fn common1() { #debug("common") } - fn common2() { #debug("common") } -} - - - -fn main() { common2(); } diff --git a/src/test/compile-fail/import-loop-2.rs b/src/test/compile-fail/import-loop-2.rs index 4040f8333f98e..7cedd8a9c4166 100644 --- a/src/test/compile-fail/import-loop-2.rs +++ b/src/test/compile-fail/import-loop-2.rs @@ -1,4 +1,4 @@ -// error-pattern:cyclic import +// error-pattern:import mod a { import b::x; diff --git a/src/test/compile-fail/import-loop.rs b/src/test/compile-fail/import-loop.rs index 6aa88db603d7e..ae1f1a7f36e1c 100644 --- a/src/test/compile-fail/import-loop.rs +++ b/src/test/compile-fail/import-loop.rs @@ -1,4 +1,4 @@ -// error-pattern: cyclic import +// error-pattern:import import y::x; diff --git a/src/test/compile-fail/import.rs b/src/test/compile-fail/import.rs index eb47db0725afd..3d3e326f1f5b8 100644 --- a/src/test/compile-fail/import.rs +++ b/src/test/compile-fail/import.rs @@ -1,4 +1,5 @@ -// error-pattern: unresolved import +// xfail-test +// error-pattern: unresolved import zed::bar; import zed::baz; mod zed { diff --git a/src/test/compile-fail/import2.rs b/src/test/compile-fail/import2.rs index 6d503a62a4722..d576f66b0641c 100644 --- a/src/test/compile-fail/import2.rs +++ b/src/test/compile-fail/import2.rs @@ -1,4 +1,4 @@ -// error-pattern: unresolved modulename +// error-pattern: unresolved import baz::zed::bar; mod baz { } mod zed { diff --git a/src/test/compile-fail/import3.rs b/src/test/compile-fail/import3.rs index 939c38fed6a4a..591ee3afa2711 100644 --- a/src/test/compile-fail/import3.rs +++ b/src/test/compile-fail/import3.rs @@ -1,4 +1,4 @@ -// error-pattern: unresolved modulename +// error-pattern: unresolved import main::bar; fn main(args: ~[str]) { #debug("foo"); } diff --git a/src/test/compile-fail/import4.rs b/src/test/compile-fail/import4.rs index d4688a4fafdb6..cf357e8539c78 100644 --- a/src/test/compile-fail/import4.rs +++ b/src/test/compile-fail/import4.rs @@ -1,4 +1,4 @@ -// error-pattern: cyclic import +// error-pattern: import mod a { import foo = b::foo; export foo; } mod b { import foo = a::foo; export foo; } diff --git a/src/test/compile-fail/import5.rs b/src/test/compile-fail/import5.rs deleted file mode 100644 index 85a77411e6828..0000000000000 --- a/src/test/compile-fail/import5.rs +++ /dev/null @@ -1,15 +0,0 @@ -// error-pattern:unresolved import - -mod m1 { - fn foo() { #debug("foo"); } -} - -mod m2 { - import m1::foo; -} - -mod m3 { - import m2::foo; -} - -fn main() { } diff --git a/src/test/compile-fail/issue-1697.rs b/src/test/compile-fail/issue-1697.rs index 63b316813ff98..4ceaa35fca2c0 100644 --- a/src/test/compile-fail/issue-1697.rs +++ b/src/test/compile-fail/issue-1697.rs @@ -1,7 +1,8 @@ +// xfail-test // Testing that we don't fail abnormally after hitting the errors import unresolved::*; //~ ERROR unresolved modulename //~^ ERROR unresolved does not name a module fn main() { -} \ No newline at end of file +} diff --git a/src/test/compile-fail/not-a-pred.rs b/src/test/compile-fail/not-a-pred.rs index 0766a8d1feed4..58082feefaab5 100644 --- a/src/test/compile-fail/not-a-pred.rs +++ b/src/test/compile-fail/not-a-pred.rs @@ -1,5 +1,5 @@ // -*- rust -*- -// error-pattern: lt is not declared pure +// error-pattern: lt fn f(a: int, b: int) : lt(a, b) { } diff --git a/src/test/compile-fail/tag-exports-2.rs b/src/test/compile-fail/tag-exports-2.rs deleted file mode 100644 index 9472783848846..0000000000000 --- a/src/test/compile-fail/tag-exports-2.rs +++ /dev/null @@ -1,18 +0,0 @@ -// error-pattern:unresolved name: lovejoy -import alder::*; - -mod alder { - export burnside; - export everett::{flanders}; - export irving::{johnson, kearney}; - export marshall::{}; - - enum burnside { couch, davis } - enum everett { flanders, glisan, hoyt } - enum irving { johnson, kearney, lovejoy } - enum marshall { northrup, overton } -} - -fn main() { - let raleigh: irving = lovejoy; -} diff --git a/src/test/compile-fail/tag-exports-3.rs b/src/test/compile-fail/tag-exports-3.rs deleted file mode 100644 index e51a0aca115c8..0000000000000 --- a/src/test/compile-fail/tag-exports-3.rs +++ /dev/null @@ -1,18 +0,0 @@ -// error-pattern:unresolved name: northrup -import alder::*; - -mod alder { - export burnside; - export everett::{flanders}; - export irving::{johnson, kearney}; - export marshall::{}; - - enum burnside { couch, davis } - enum everett { flanders, glisan, hoyt } - enum irving { johnson, kearney, lovejoy } - enum marshall { northrup, overton } -} - -fn main() { - let savier: marshall = northrup; -} diff --git a/src/test/compile-fail/tag-exports.rs b/src/test/compile-fail/tag-exports.rs deleted file mode 100644 index 18965c9184204..0000000000000 --- a/src/test/compile-fail/tag-exports.rs +++ /dev/null @@ -1,18 +0,0 @@ -// error-pattern:unresolved name: glisan -import alder::*; - -mod alder { - export burnside; - export everett::{flanders}; - export irving::{johnson, kearney}; - export marshall::{}; - - enum burnside { couch, davis } - enum everett { flanders, glisan, hoyt } - enum irving { johnson, kearney, lovejoy } - enum marshall { northrup, overton } -} - -fn main() { - let quimby: everett = glisan; -} diff --git a/src/test/run-pass/issue2170exe.rs b/src/test/run-pass/issue2170exe.rs index 34e51843070d5..dafa6b8556a49 100644 --- a/src/test/run-pass/issue2170exe.rs +++ b/src/test/run-pass/issue2170exe.rs @@ -3,5 +3,5 @@ use issue2170lib; fn main() { - let _ = issue2170lib::rsrc(2i32); + // let _ = issue2170lib::rsrc(2i32); } diff --git a/src/test/run-pass/tag-exports.rs b/src/test/run-pass/tag-exports.rs index 6e5d07299c6d2..57937855ca4da 100644 --- a/src/test/run-pass/tag-exports.rs +++ b/src/test/run-pass/tag-exports.rs @@ -2,6 +2,7 @@ import alder::*; mod alder { export burnside; + export couch; export everett; export flanders; export irving; From b5b8f5efccc3f39be807dd2e060e913ff263b73d Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Fri, 6 Jul 2012 18:16:09 -0400 Subject: [PATCH 16/32] change borrowck error msg: 'declared in outer block' -> 'captured in a closure' --- src/rustc/middle/borrowck.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs index 38559aec28dd2..ec2002f8f6b18 100644 --- a/src/rustc/middle/borrowck.rs +++ b/src/rustc/middle/borrowck.rs @@ -467,7 +467,9 @@ impl to_str_methods for borrowck_ctxt { cat_special(sk_method) { "method" } cat_special(sk_static_item) { "static item" } cat_special(sk_self) { "self reference" } - cat_special(sk_heap_upvar) { "variable declared in an outer block" } + cat_special(sk_heap_upvar) { + "captured outer variable from within a heap closure" + } cat_rvalue { "non-lvalue" } cat_local(_) { mut_str + " local variable" } cat_binding(_) { "pattern binding" } @@ -475,7 +477,7 @@ impl to_str_methods for borrowck_ctxt { cat_deref(_, _, pk) { #fmt["dereference of %s %s pointer", mut_str, self.pk_to_sigil(pk)] } cat_stack_upvar(_) { - mut_str + " variable declared in an outer block" + "captured " + mut_str + " variable from within a stack closure" } cat_comp(_, comp_field(*)) { mut_str + " field" } cat_comp(_, comp_tuple) { "tuple content" } From f9cb04f6fa6c23e98322d2aef5e704bfdbcacb7b Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Fri, 6 Jul 2012 22:15:15 -0400 Subject: [PATCH 17/32] vim: hilight option, either, libc types+constants --- src/etc/vim/syntax/rust.vim | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/etc/vim/syntax/rust.vim b/src/etc/vim/syntax/rust.vim index 26321bc5d104d..02cdae07c04c7 100644 --- a/src/etc/vim/syntax/rust.vim +++ b/src/etc/vim/syntax/rust.vim @@ -1,7 +1,8 @@ " Vim syntax file " Language: Rust " Maintainer: Patrick Walton -" Last Change: 2010 Oct 13 +" Maintainer: Ben Blum +" Last Change: 2012 Jul 06 if version < 600 syntax clear @@ -12,8 +13,8 @@ endif syn keyword rustAssert assert syn match rustAssert "assert\(\w\)*" syn keyword rustKeyword alt as break -syn keyword rustKeyword check claim cont const copy else export extern fail -syn keyword rustKeyword do drop for if impl import in let log +syn keyword rustKeyword check claim cont const copy do drop else export extern fail +syn keyword rustKeyword for if impl import in let log syn keyword rustKeyword loop mod mut new of pure syn keyword rustKeyword ret self to unchecked syn match rustKeyword "unsafe" " Allows also matching unsafe::foo() @@ -30,6 +31,16 @@ syn keyword rustKeyword m32 m64 m128 f80 f16 f128 syn keyword rustType any int uint float char bool u8 u16 u32 u64 f32 syn keyword rustType f64 i8 i16 i32 i64 str +syn keyword rustType option either + +" Types from libc +syn keyword rustType c_float c_double c_void FILE fpos_t +syn keyword rustType DIR dirent +syn keyword rustType c_char c_schar c_uchar +syn keyword rustType c_short c_ushort c_int c_uint c_long c_ulong +syn keyword rustType size_t ptrdiff_t clock_t time_t +syn keyword rustType c_longlong c_ulonglong intptr_t uintptr_t +syn keyword rustType off_t dev_t ino_t pid_t mode_t ssize_t syn keyword rustBoolean true false @@ -37,9 +48,19 @@ syn keyword rustConstant some none " option syn keyword rustConstant left right " either syn keyword rustConstant ok err " result syn keyword rustConstant success failure " task -" syn keyword rustConstant cons nil " list +syn keyword rustConstant cons nil " list " syn keyword rustConstant empty node " tree +" Constants from libc +syn keyword rustConstant EXIT_FAILURE EXIT_SUCCESS RAND_MAX +syn keyword rustConstant EOF SEEK_SET SEEK_CUR SEEK_END _IOFBF _IONBF +syn keyword rustConstant _IOLBF BUFSIZ FOPEN_MAX FILENAME_MAX L_tmpnam +syn keyword rustConstant TMP_MAX O_RDONLY O_WRONLY O_RDWR O_APPEND O_CREAT +syn keyword rustConstant O_EXCL O_TRUNC S_IFIFO S_IFCHR S_IFBLK S_IFDIR +syn keyword rustConstant S_IFREG S_IFMT S_IEXEC S_IWRITE S_IREAD S_IRWXU +syn keyword rustConstant S_IXUSR S_IWUSR S_IRUSR F_OK R_OK W_OK X_OK +syn keyword rustConstant STDIN_FILENO STDOUT_FILENO STDERR_FILENO + " If foo::bar changes to foo.bar, change this ("::" to "\."). " If foo::bar changes to Foo::bar, change this (first "\w" to "\u"). syn match rustModPath "\w\(\w\)*::[^<]"he=e-3,me=e-3 From a856bccdc647330624fa36a25190aa63e1274379 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 6 Jul 2012 20:45:06 -0700 Subject: [PATCH 18/32] Revert "rustc: Switch to the new resolution pass" This reverts commit c4af6e92fbae171c56a4e68666025725555fc9d8. Branch was burning...many, many unresolved imports. --- src/libcore/task.rs | 6 - src/libstd/net_ip.rs | 3 +- src/libsyntax/parse.rs | 6 +- src/libsyntax/parse/comments.rs | 1 - src/libsyntax/parse/parser.rs | 42 +- src/rustc/driver/driver.rs | 23 +- src/rustc/metadata/loader.rs | 1 - src/rustc/middle/resolve3.rs | 450 +++++------------- src/rustc/middle/ty.rs | 7 - src/rustc/middle/typeck.rs | 2 - src/rustdoc/config.rs | 2 - src/test/compile-fail/ambig_impl_2_exe.rs | 4 +- src/test/compile-fail/bad-tag-export-2.rs | 11 + src/test/compile-fail/bad-tag-export-3.rs | 13 + src/test/compile-fail/bad-tag-export-4.rs | 12 + src/test/compile-fail/bad-tag-export.rs | 14 + .../cap-clause-unresolved-copy.rs | 2 +- .../cap-clause-unresolved-move.rs | 2 +- .../class-implements-bad-iface.rs | 4 +- src/test/compile-fail/class-implements-int.rs | 4 +- .../cross-crate-glob-collision.rs | 13 + src/test/compile-fail/export-import.rs | 2 +- src/test/compile-fail/iface-test.rs | 6 +- src/test/compile-fail/import-from-dup.rs | 14 + src/test/compile-fail/import-from-missing.rs | 2 +- src/test/compile-fail/import-glob-circular.rs | 2 +- src/test/compile-fail/import-glob-multiple.rs | 20 + src/test/compile-fail/import-loop-2.rs | 2 +- src/test/compile-fail/import-loop.rs | 2 +- src/test/compile-fail/import.rs | 3 +- src/test/compile-fail/import2.rs | 2 +- src/test/compile-fail/import3.rs | 2 +- src/test/compile-fail/import4.rs | 2 +- src/test/compile-fail/import5.rs | 15 + src/test/compile-fail/issue-1697.rs | 3 +- src/test/compile-fail/not-a-pred.rs | 2 +- src/test/compile-fail/tag-exports-2.rs | 18 + src/test/compile-fail/tag-exports-3.rs | 18 + src/test/compile-fail/tag-exports.rs | 18 + src/test/run-pass/issue2170exe.rs | 2 +- src/test/run-pass/tag-exports.rs | 1 - 41 files changed, 328 insertions(+), 430 deletions(-) create mode 100644 src/test/compile-fail/bad-tag-export-2.rs create mode 100644 src/test/compile-fail/bad-tag-export-3.rs create mode 100644 src/test/compile-fail/bad-tag-export-4.rs create mode 100644 src/test/compile-fail/bad-tag-export.rs create mode 100644 src/test/compile-fail/cross-crate-glob-collision.rs create mode 100644 src/test/compile-fail/import-from-dup.rs create mode 100644 src/test/compile-fail/import-glob-multiple.rs create mode 100644 src/test/compile-fail/import5.rs create mode 100644 src/test/compile-fail/tag-exports-2.rs create mode 100644 src/test/compile-fail/tag-exports-3.rs create mode 100644 src/test/compile-fail/tag-exports.rs diff --git a/src/libcore/task.rs b/src/libcore/task.rs index 855a090e47453..f41e24c623f70 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -63,12 +63,6 @@ export local_data_get; export local_data_set; export local_data_modify; -export single_threaded; -export thread_per_core; -export thread_per_task; -export manual_threads; -export osmain; - /* Data types */ /// A handle to a task diff --git a/src/libstd/net_ip.rs b/src/libstd/net_ip.rs index 4585b4564b571..2353a983dc2bd 100644 --- a/src/libstd/net_ip.rs +++ b/src/libstd/net_ip.rs @@ -25,7 +25,6 @@ export ip_addr, parse_addr_err; export format_addr; export v4, v6; export get_addr; -export ipv4, ipv6; /// An IP address enum ip_addr { @@ -390,4 +389,4 @@ mod test { let ga_result = get_addr(localhost_name, iotask); assert result::is_err(ga_result); } -} +} \ No newline at end of file diff --git a/src/libsyntax/parse.rs b/src/libsyntax/parse.rs index 7a9ec6f32a446..b2d06311e673a 100644 --- a/src/libsyntax/parse.rs +++ b/src/libsyntax/parse.rs @@ -16,8 +16,10 @@ export parse_from_source_str; // unresolved import errors. Maybe resolve3 will fix it. import common::*; import parser::parser; -import attr::parser_attr; -import common::parser_common; +//import attr::parser_attr; +import attr::*; //resolve bug? +//import common::parser_common; +import common::*; //resolve bug? import ast::node_id; import util::interner; // FIXME (#1935): resolve badness diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs index bd164402c1d79..e188331dd2490 100644 --- a/src/libsyntax/parse/comments.rs +++ b/src/libsyntax/parse/comments.rs @@ -9,7 +9,6 @@ export lit; export cmnt_style; export gather_comments_and_literals; export is_doc_comment, doc_comment_style, strip_doc_comment_decoration; -export isolated, trailing, mixed, blank_line; enum cmnt_style { isolated, // No code on either side of each line of the comment diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6727ebe4c7753..c94e2acbb2b44 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -12,47 +12,7 @@ import common::{seq_sep_trailing_disallowed, seq_sep_trailing_allowed, seq_sep_none, token_to_str}; import dvec::{dvec, extensions}; import vec::{push}; -import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute, - bitand, bitor, bitxor, blk, blk_check_mode, bound_const, - bound_copy, bound_send, bound_trait, box, by_copy, by_move, - by_mutbl_ref, by_ref, by_val, capture_clause, capture_item, - carg_base, carg_ident, cdir_dir_mod, cdir_src_mod, - cdir_view_item, checked_expr, claimed_expr, class_immutable, - class_member, class_method, class_mutable, constr, constr_arg, - constr_general, crate, crate_cfg, crate_directive, decl, - decl_item, decl_local, default_blk, deref, div, expl, expr, - expr_, expr_addr_of, expr_alt, expr_assert, expr_assign, - expr_assign_op, expr_binary, expr_block, expr_break, expr_call, - expr_cast, expr_check, expr_cont, expr_copy, expr_do_body, - expr_fail, expr_field, expr_fn, expr_fn_block, expr_if, - expr_if_check, expr_index, expr_lit, expr_log, expr_loop, - expr_loop_body, expr_mac, expr_move, expr_new, expr_path, - expr_rec, expr_ret, expr_swap, expr_tup, expr_unary, expr_vec, - expr_vstore, expr_while, extern_fn, field, fn_decl, foreign_item, - foreign_item_fn, foreign_mod, ident, impure_fn, infer, - init_assign, init_move, initializer, instance_var, item, item_, - item_class, item_const, item_enum, item_fn, item_foreign_mod, - item_impl, item_mod, item_trait, item_ty, lit, lit_, lit_bool, - lit_float, lit_int, lit_int_unsuffixed, lit_nil, lit_str, - lit_uint, local, m_const, m_imm, m_mutbl, mac_, mac_aq, - mac_ellipsis, mac_embed_block, mac_embed_type, mac_invoc, - mac_invoc_tt, mac_var, matcher, method, mode, mt, mtc_bb, - mtc_rep, mtc_tok, mul, mutability, neg, noreturn, not, pat, - pat_box, pat_enum, pat_ident, pat_lit, pat_range, pat_rec, - pat_tup, pat_uniq, pat_wild, path, private, proto, proto_any, - proto_bare, proto_block, proto_box, proto_uniq, public, pure_fn, - purity, re_anon, re_named, region, region_param, rem, ret_style, - return_val, rp_none, rp_self, shl, shr, stmt, stmt_decl, - stmt_expr, stmt_semi, subtract, token_tree, trait_ref, tt_delim, - tt_dotdotdot, tt_flat, tt_interpolate, ty, ty_, ty_bot, ty_box, - ty_constr, ty_constr_, ty_constr_arg, ty_field, ty_fn, ty_infer, - ty_mac, ty_method, ty_nil, ty_param, ty_path, ty_ptr, ty_rec, - ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec, ty_vstore, - unchecked_blk, uniq, unsafe_blk, unsafe_fn, variant, view_item, - view_item_, view_item_export, view_item_import, view_item_use, - view_path, view_path_glob, view_path_list, view_path_simple, - visibility, vstore, vstore_box, vstore_fixed, vstore_slice, - vstore_uniq}; +import ast::*; export file_type; export parser; diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs index 6c6247fabe3c6..799f34377ede4 100644 --- a/src/rustc/driver/driver.rs +++ b/src/rustc/driver/driver.rs @@ -168,9 +168,26 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg, session::sess_os_to_meta_os(sess.targ_cfg.os), sess.opts.static)); - let { def_map: def_map, exp_map: exp_map, impl_map: impl_map } = - time(time_passes, "fast resolution", || - middle::resolve3::resolve_crate(sess, ast_map, crate)); + let mut def_map; + let mut impl_map; + let mut exp_map; + if sess.fast_resolve() { + let { def_map: fast_dm, exp_map: fast_em, impl_map: fast_im } = + time(time_passes, "fast resolution", || + middle::resolve3::resolve_crate(sess, ast_map, crate)); + + def_map = fast_dm; + impl_map = fast_im; + exp_map = fast_em; + } else { + let { def_map: normal_dm, exp_map: normal_em, impl_map: normal_im } = + time(time_passes, "resolution", || + resolve::resolve_crate(sess, ast_map, crate)); + + def_map = normal_dm; + impl_map = normal_im; + exp_map = normal_em; + } let freevars = time(time_passes, "freevar finding", || freevars::annotate_freevars(def_map, crate)); diff --git a/src/rustc/metadata/loader.rs b/src/rustc/metadata/loader.rs index d92154eb891ec..63f3658a4e93e 100644 --- a/src/rustc/metadata/loader.rs +++ b/src/rustc/metadata/loader.rs @@ -9,7 +9,6 @@ import filesearch::filesearch; import io::writer_util; export os; -export os_macos, os_win32, os_linux, os_freebsd; export ctxt; export load_library_crate; export list_file_metadata; diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs index 69d3a25d5a156..6ca613d528103 100644 --- a/src/rustc/middle/resolve3.rs +++ b/src/rustc/middle/resolve3.rs @@ -2,7 +2,6 @@ import driver::session::session; import metadata::csearch::{each_path, get_impls_for_mod, lookup_defs}; import metadata::cstore::find_use_stmt_cnum; import metadata::decoder::{def_like, dl_def, dl_field, dl_impl}; -import middle::lint::{error, ignore, level, unused_imports, warn}; import syntax::ast::{_mod, arm, blk, bound_const, bound_copy, bound_trait}; import syntax::ast::{bound_send, capture_clause, class_ctor, class_dtor}; import syntax::ast::{class_member, class_method, crate, crate_num, decl_item}; @@ -144,10 +143,7 @@ enum TypeParameters/& { // The index at the method site will be 1, because the // outer T had index 0. - uint, - - // The kind of the rib used for type parameters. - RibKind) + uint) } // The rib kind controls the translation of argument or local definitions @@ -156,13 +152,9 @@ enum TypeParameters/& { enum RibKind { // No translation needs to be applied. NormalRibKind, - // We passed through a function scope at the given node ID. Translate // upvars as appropriate. - FunctionRibKind(node_id), - - // We passed through a function *item* scope. Disallow upvars. - OpaqueFunctionRibKind + FunctionRibKind(node_id) } // The X-ray flag indicates that a context has the X-ray privilege, which @@ -177,17 +169,6 @@ enum XrayFlag { Xray //< Private items can be accessed. } -enum AllowCapturingSelfFlag { - AllowCapturingSelf, //< The "self" definition can be captured. - DontAllowCapturingSelf, //< The "self" definition cannot be captured. -} - -enum EnumVariantOrConstResolution { - FoundEnumVariant(def), - FoundConst, - EnumVariantOrConstNotFound -} - // FIXME (issue #2550): Should be a class but then it becomes not implicitly // copyable due to a kind bug. @@ -277,15 +258,10 @@ class Rib { class ImportDirective { let module_path: @dvec; let subclass: @ImportDirectiveSubclass; - let span: span; - - new(module_path: @dvec, - subclass: @ImportDirectiveSubclass, - span: span) { + new(module_path: @dvec, subclass: @ImportDirectiveSubclass) { self.module_path = module_path; self.subclass = subclass; - self.span = span; } } @@ -301,8 +277,6 @@ class Target { } class ImportResolution { - let span: span; - // The number of outstanding references to this name. When this reaches // zero, outside modules can count on the targets being correct. Before // then, all bets are off; future imports could override this name. @@ -314,19 +288,13 @@ class ImportResolution { let mut type_target: option; let mut impl_target: @dvec<@Target>; - let mut used: bool; - - new(span: span) { - self.span = span; - + new() { self.outstanding_references = 0u; self.module_target = none; self.value_target = none; self.type_target = none; self.impl_target = @dvec(); - - self.used = false; } fn target_for_namespace(namespace: Namespace) -> option { @@ -430,18 +398,10 @@ pure fn is_none(x: option) -> bool { } } -fn unused_import_lint_level(session: session) -> level { - for session.opts.lint_opts.each |lint_option_pair| { - let (lint_type, lint_level) = lint_option_pair; - if lint_type == unused_imports { - ret lint_level; - } - } - ret ignore; -} - -// Records the definitions (at most one for each namespace) that a name is -// bound to. +/** + * Records the definitions (at most one for each namespace) that a name is + * bound to. + */ class NameBindings { let mut module_def: ModuleDef; //< Meaning in the module namespace. let mut type_def: option; //< Meaning in the type namespace. @@ -589,8 +549,6 @@ class Resolver { let graph_root: @NameBindings; - let unused_import_lint_level: level; - // The number of imports that are currently unresolved. let mut unresolved_imports: uint; @@ -636,8 +594,6 @@ class Resolver { (*self.graph_root).define_module(NoParentLink, some({ crate: 0, node: 0 })); - self.unused_import_lint_level = unused_import_lint_level(session); - self.unresolved_imports = 0u; self.current_module = (*self.graph_root).get_module(); @@ -658,21 +614,10 @@ class Resolver { /// The main name resolution procedure. fn resolve(this: @Resolver) { self.build_reduced_graph(this); - self.session.abort_if_errors(); - self.resolve_imports(); - self.session.abort_if_errors(); - self.record_exports(); - self.session.abort_if_errors(); - self.build_impl_scopes(); - self.session.abort_if_errors(); - self.resolve_crate(); - self.session.abort_if_errors(); - - self.check_for_unused_imports_if_necessary(); } // @@ -1000,8 +945,7 @@ class Resolver { source_atom); self.build_import_directive(module, module_path, - subclass, - view_path.span); + subclass); } view_path_list(_, source_idents, _) { for source_idents.each |source_ident| { @@ -1010,15 +954,13 @@ class Resolver { let subclass = @SingleImport(atom, atom); self.build_import_directive(module, module_path, - subclass, - view_path.span); + subclass); } } view_path_glob(_, _) { self.build_import_directive(module, module_path, - @GlobImport, - view_path.span); + @GlobImport); } } } @@ -1124,8 +1066,7 @@ class Resolver { do self.with_type_parameter_rib (HasTypeParameters(&type_parameters, foreign_item.id, - 0u, - NormalRibKind)) || { + 0u)) || { visit_foreign_item(foreign_item, new_parent, visitor); } @@ -1351,10 +1292,9 @@ class Resolver { /// Creates and adds an import directive to the given module. fn build_import_directive(module: @Module, module_path: @dvec, - subclass: @ImportDirectiveSubclass, - span: span) { + subclass: @ImportDirectiveSubclass) { - let directive = @ImportDirective(module_path, subclass, span); + let directive = @ImportDirective(module_path, subclass); module.imports.push(directive); // Bump the reference count on the name. Or, if this is a glob, set @@ -1367,7 +1307,7 @@ class Resolver { resolution.outstanding_references += 1u; } none { - let resolution = @ImportResolution(span); + let resolution = @ImportResolution(); resolution.outstanding_references = 1u; module.import_resolutions.insert(target, resolution); } @@ -1463,8 +1403,9 @@ class Resolver { alt self.resolve_import_for_module(module, import_directive) { Failed { // We presumably emitted an error. Continue. - self.session.span_err(import_directive.span, - "failed to resolve import"); + // XXX: span_err + self.session.err(#fmt("failed to resolve import in: %s", + self.module_to_str(module))); } Indeterminate { // Bail out. We'll come around next time. @@ -1509,8 +1450,7 @@ class Resolver { // First, resolve the module path for the directive, if necessary. alt self.resolve_module_path_for_import(module, module_path, - NoXray, - import_directive.span) { + NoXray) { Failed { resolution_result = Failed; @@ -1531,11 +1471,9 @@ class Resolver { source); } GlobImport { - let span = import_directive.span; resolution_result = self.resolve_glob_import(module, - containing_module, - span); + containing_module); } } } @@ -1672,7 +1610,6 @@ class Resolver { some(import_resolution) if import_resolution.outstanding_references == 0u { - fn get_binding(import_resolution: @ImportResolution, namespace: Namespace) -> NamespaceResult { @@ -1683,7 +1620,6 @@ class Resolver { ret UnboundResult; } some(target) { - import_resolution.used = true; ret BoundResult(target.target_module, target.bindings); } @@ -1794,9 +1730,7 @@ class Resolver { * succeeds or bails out (as importing * from an empty module or a module * that exports nothing is valid). */ - fn resolve_glob_import(module: @Module, - containing_module: @Module, - span: span) + fn resolve_glob_import(module: @Module, containing_module: @Module) -> ResolveResult<()> { // This function works in a highly imperative manner; it eagerly adds @@ -1833,8 +1767,7 @@ class Resolver { alt module.import_resolutions.find(atom) { none { // Simple: just copy the old import resolution. - let new_import_resolution = - @ImportResolution(target_import_resolution.span); + let new_import_resolution = @ImportResolution(); new_import_resolution.module_target = copy target_import_resolution.module_target; new_import_resolution.value_target = @@ -1895,17 +1828,12 @@ class Resolver { // Add all children from the containing module. for containing_module.children.each |atom, name_bindings| { - if !self.name_is_exported(containing_module, atom) { - #debug("(resolving glob import) name '%s' is unexported", - *(*self.atom_table).atom_to_str(atom)); - cont; - } let mut dest_import_resolution; alt module.import_resolutions.find(atom) { none { // Create a new import resolution from this child. - dest_import_resolution = @ImportResolution(span); + dest_import_resolution = @ImportResolution(); module.import_resolutions.insert (atom, dest_import_resolution); } @@ -1951,8 +1879,7 @@ class Resolver { fn resolve_module_path_from_root(module: @Module, module_path: @dvec, index: uint, - xray: XrayFlag, - span: span) + xray: XrayFlag) -> ResolveResult<@Module> { let mut search_module = module; @@ -1969,7 +1896,10 @@ class Resolver { xray) { Failed { - self.session.span_err(span, "unresolved name"); + // XXX: span_err + self.session.err(#fmt("module resolution failed: %s", + *(*self.atom_table) + .atom_to_str(name))); ret Failed; } Indeterminate { @@ -1982,10 +1912,10 @@ class Resolver { alt target.bindings.module_def { NoModuleDef { // Not a module. - self.session.span_err(span, - #fmt("not a module: %s", - *(*self.atom_table). - atom_to_str(name))); + // XXX: span_err + self.session.err(#fmt("not a module: %s", + *(*self.atom_table). + atom_to_str(name))); ret Failed; } ModuleDef(module) { @@ -2007,8 +1937,7 @@ class Resolver { */ fn resolve_module_path_for_import(module: @Module, module_path: @dvec, - xray: XrayFlag, - span: span) + xray: XrayFlag) -> ResolveResult<@Module> { let module_path_len = (*module_path).len(); @@ -2026,7 +1955,10 @@ class Resolver { let mut search_module; alt self.resolve_module_in_lexical_scope(module, first_element) { Failed { - self.session.span_err(span, "unresolved name"); + // XXX: span_err + self.session.err(#fmt("unresolved name: %s", + *(*self.atom_table). + atom_to_str(first_element))); ret Failed; } Indeterminate { @@ -2042,8 +1974,7 @@ class Resolver { ret self.resolve_module_path_from_root(search_module, module_path, 1u, - xray, - span); + xray); } fn resolve_item_in_lexical_scope(module: @Module, @@ -2087,7 +2018,6 @@ class Resolver { namespace); } some(target) { - import_resolution.used = true; ret Success(copy target); } } @@ -2227,7 +2157,6 @@ class Resolver { some(target) { #debug("(resolving name in module) resolved to \ import"); - import_resolution.used = true; ret Success(copy target); } } @@ -2387,7 +2316,8 @@ class Resolver { if is_none(module_result) && is_none(value_result) && is_none(type_result) && is_none(impl_result) { - self.session.span_err(import_directive.span, "unresolved import"); + // XXX: span_err, better error + self.session.err("couldn't find anything with that name"); ret Failed; } @@ -2431,8 +2361,13 @@ class Resolver { let index = module.resolved_import_count; let import_count = module.imports.len(); if index != import_count { - self.session.span_err(module.imports.get_elt(index).span, - "unresolved import"); + let module_path = module.imports.get_elt(index).module_path; + + // XXX: span_err + self.session.err(#fmt("unresolved import in %s: %s", + self.module_to_str(module), + *(*self.atom_table) + .atoms_to_str((*module_path).get()))); } // Descend into children and anonymous children. @@ -2517,8 +2452,7 @@ class Resolver { alt self.resolve_definition_of_name_in_module(module, name, - namespace, - Xray) { + namespace) { NoNameDefinition { // Nothing to do. } @@ -2577,7 +2511,7 @@ class Resolver { for module.children.each |_atom, child_name_bindings| { alt (*child_name_bindings).get_module_if_available() { none { - // Nothing to do. + /* Nothing to do. */ } some(child_module) { self.build_impl_scopes_for_module_subtree(child_module); @@ -2643,7 +2577,10 @@ class Resolver { // AST resolution // - // We maintain a list of value ribs and type ribs. + // We maintain a list of value ribs and type ribs. Since ribs are + // somewhat expensive to allocate, we try to avoid creating ribs unless + // we know we need to. For instance, we don't allocate a type rib for + // a function with no type parameters. // // Simultaneously, we keep track of the current position in the module // graph in the `current_module` pointer. When we go to resolve a name in @@ -2697,30 +2634,18 @@ class Resolver { // Wraps the given definition in the appropriate number of `def_upvar` // wrappers. - fn upvarify(ribs: @dvec<@Rib>, rib_index: uint, def_like: def_like, - span: span, allow_capturing_self: AllowCapturingSelfFlag) - -> option { + fn upvarify(ribs: @dvec<@Rib>, rib_index: uint, def_like: def_like) + -> def_like { let mut def; - let mut is_ty_param; - alt def_like { dl_def(d @ def_local(*)) | dl_def(d @ def_upvar(*)) | - dl_def(d @ def_arg(*)) | dl_def(d @ def_binding(*)) { - def = d; - is_ty_param = false; - } - dl_def(d @ def_ty_param(*)) { + dl_def(d @ def_arg(*)) | dl_def(d @ def_self(*)) | + dl_def(d @ def_binding(*)) { def = d; - is_ty_param = true; - } - dl_def(d @ def_self(*)) - if allow_capturing_self == DontAllowCapturingSelf { - def = d; - is_ty_param = false; } _ { - ret some(def_like); + ret def_like; } } @@ -2732,43 +2657,19 @@ class Resolver { // Nothing to do. Continue. } FunctionRibKind(function_id) { - if !is_ty_param { - def = def_upvar(def_id_of_def(def).node, - @def, - function_id); - } - } - OpaqueFunctionRibKind { - if !is_ty_param { - // This was an attempt to access an upvar inside a - // named function item. This is not allowed, so we - // report an error. - - self.session.span_err(span, - "attempted dynamic environment-\ - capture"); - } else { - // This was an attempt to use a type parameter outside - // its scope. - - self.session.span_err(span, - "attempt to use a type \ - argument out of scope"); - } - - ret none; + def = def_upvar(def_id_of_def(def).node, + @def, + function_id); } } rib_index += 1u; } - ret some(dl_def(def)); + ret dl_def(def); } - fn search_ribs(ribs: @dvec<@Rib>, name: Atom, span: span, - allow_capturing_self: AllowCapturingSelfFlag) - -> option { + fn search_ribs(ribs: @dvec<@Rib>, name: Atom) -> option { // XXX: This should not use a while loop. // XXX: Try caching? @@ -2779,8 +2680,7 @@ class Resolver { let rib = (*ribs).get_elt(i); alt rib.bindings.find(name) { some(def_like) { - ret self.upvarify(ribs, i, def_like, span, - allow_capturing_self); + ret some(self.upvarify(ribs, i, def_like)); } none { // Continue. @@ -2833,8 +2733,7 @@ class Resolver { item_enum(_, type_parameters, _) | item_ty(_, type_parameters, _) { do self.with_type_parameter_rib - (HasTypeParameters(&type_parameters, item.id, 0u, - NormalRibKind)) + (HasTypeParameters(&type_parameters, item.id, 0u)) || { visit_item(item, (), visitor); @@ -2861,8 +2760,7 @@ class Resolver { // Create a new rib for the interface-wide type parameters. do self.with_type_parameter_rib - (HasTypeParameters(&type_parameters, item.id, 0u, - NormalRibKind)) + (HasTypeParameters(&type_parameters, item.id, 0u)) || { for methods.each |method| { @@ -2874,8 +2772,7 @@ class Resolver { do self.with_type_parameter_rib (HasTypeParameters(&method.tps, item.id, - type_parameters.len(), - NormalRibKind)) + type_parameters.len())) || { // Resolve the method-specific type parameters. @@ -2922,9 +2819,7 @@ class Resolver { do self.with_type_parameter_rib (HasTypeParameters(&type_parameters, foreign_item.id, - 0u, - OpaqueFunctionRibKind)) - || { + 0u)) || { visit_foreign_item(foreign_item, (), visitor); @@ -2949,13 +2844,11 @@ class Resolver { self.session.main_fn = some((item.id, item.span)); } - self.resolve_function(OpaqueFunctionRibKind, + self.resolve_function(NormalRibKind, some(@fn_decl), - HasTypeParameters - (&ty_params, - item.id, - 0u, - OpaqueFunctionRibKind), + HasTypeParameters(&ty_params, + item.id, + 0u), block, NoSelfBinding, NoCaptureClause, @@ -2976,10 +2869,10 @@ class Resolver { fn with_type_parameter_rib(type_parameters: TypeParameters, f: fn()) { alt type_parameters { - HasTypeParameters(type_parameters, node_id, initial_index, - rib_kind) { + HasTypeParameters(type_parameters, node_id, initial_index) + if (*type_parameters).len() >= 1u { - let function_type_rib = @Rib(rib_kind); + let function_type_rib = @Rib(NormalRibKind); (*self.type_ribs).push(function_type_rib); for (*type_parameters).eachi |index, type_parameter| { @@ -2992,7 +2885,7 @@ class Resolver { } } - NoTypeParameters { + HasTypeParameters(*) | NoTypeParameters { // Nothing to do. } } @@ -3000,11 +2893,13 @@ class Resolver { f(); alt type_parameters { - HasTypeParameters(type_parameters, _, _, _) { + HasTypeParameters(type_parameters, _, _) + if (*type_parameters).len() >= 1u { + (*self.type_ribs).pop(); } - NoTypeParameters { + HasTypeParameters(*) | NoTypeParameters { // Nothing to do. } } @@ -3028,11 +2923,11 @@ class Resolver { for (*capture_clause).each |capture_item| { alt self.resolve_identifier(capture_item.name, ValueNS, - true, - capture_item.span) { + true) { none { self.session.span_err(capture_item.span, - "unresolved name in \ + "use of undeclared \ + identifier in \ capture clause"); } some(def) { @@ -3054,7 +2949,7 @@ class Resolver { NoTypeParameters { // Continue. } - HasTypeParameters(type_parameters, _, _, _) { + HasTypeParameters(type_parameters, _, _) { self.resolve_type_parameters(*type_parameters, visitor); } } @@ -3097,8 +2992,8 @@ class Resolver { false, visitor) { none { self.session.span_err(constraint.span, - #fmt("unresolved name: %s", - *constraint.node.path.idents.last())); + "use of undeclared \ + constraint"); } some(def) { self.record_def(constraint.node.id, def); @@ -3149,8 +3044,7 @@ class Resolver { let outer_type_parameter_count = (*type_parameters).len(); let borrowed_type_parameters: &~[ty_param] = &*type_parameters; do self.with_type_parameter_rib(HasTypeParameters - (borrowed_type_parameters, id, 0u, - NormalRibKind)) + (borrowed_type_parameters, id, 0u)) || { // Resolve the type parameters. @@ -3161,8 +3055,8 @@ class Resolver { alt self.resolve_path(interface.path, TypeNS, true, visitor) { none { self.session.span_err(interface.path.span, - "attempt to implement a \ - nonexistent interface"); + "attempt to implement an \ + unknown interface"); } some(def) { // Write a mapping from the interface ID to the @@ -3189,8 +3083,7 @@ class Resolver { let type_parameters = HasTypeParameters(borrowed_method_type_parameters, method.id, - outer_type_parameter_count, - NormalRibKind); + outer_type_parameter_count); self.resolve_function(NormalRibKind, some(@method.decl), type_parameters, @@ -3246,8 +3139,7 @@ class Resolver { let outer_type_parameter_count = type_parameters.len(); let borrowed_type_parameters: &~[ty_param] = &type_parameters; do self.with_type_parameter_rib(HasTypeParameters - (borrowed_type_parameters, id, 0u, - NormalRibKind)) + (borrowed_type_parameters, id, 0u)) || { // Resolve the type parameters. @@ -3286,8 +3178,7 @@ class Resolver { HasTypeParameters (borrowed_type_parameters, method.id, - outer_type_parameter_count, - NormalRibKind), + outer_type_parameter_count), method.body, HasSelfBinding(method.self_id), NoCaptureClause, @@ -3482,32 +3373,19 @@ class Resolver { // such a variant is simply disallowed (since it's rarely // what you want). + // XXX: This restriction is not yet implemented. + let atom = (*self.atom_table).intern(path.idents[0]); - alt self.resolve_enum_variant_or_const(atom) { - FoundEnumVariant(def) if mode == RefutableMode { + alt self.resolve_enum_variant(atom) { + some(def) { #debug("(resolving pattern) resolving '%s' to \ enum variant", *path.idents[0]); self.record_def(pattern.id, def); } - FoundEnumVariant(_) { - self.session.span_err(pattern.span, - #fmt("declaration of `%s` \ - shadows an enum \ - that's in scope", - *(*self.atom_table). - atom_to_str - (atom))); - } - FoundConst { - self.session.span_err(pattern.span, - "pattern variable \ - conflicts with a constant \ - in scope"); - } - EnumVariantOrConstNotFound { + none { #debug("(resolving pattern) binding '%s'", *path.idents[0]); @@ -3579,7 +3457,7 @@ class Resolver { } none { self.session.span_err(path.span, - "unresolved enum variant"); + "undeclared enum variant"); } } @@ -3596,9 +3474,7 @@ class Resolver { } } - fn resolve_enum_variant_or_const(name: Atom) - -> EnumVariantOrConstResolution { - + fn resolve_enum_variant(name: Atom) -> option { alt self.resolve_item_in_lexical_scope(self.current_module, name, ValueNS) { @@ -3610,13 +3486,10 @@ class Resolver { of name bindings with no def?!"; } some(def @ def_variant(*)) { - ret FoundEnumVariant(def); - } - some(def_const(*)) { - ret FoundConst; + ret some(def); } some(_) { - ret EnumVariantOrConstNotFound; + ret none; } } } @@ -3626,7 +3499,7 @@ class Resolver { } Failed { - ret EnumVariantOrConstNotFound; + ret none; } } } @@ -3658,20 +3531,16 @@ class Resolver { ret self.resolve_identifier(path.idents.last(), namespace, - check_ribs, - path.span); + check_ribs); } fn resolve_identifier(identifier: ident, namespace: Namespace, - check_ribs: bool, - span: span) + check_ribs: bool) -> option { if check_ribs { - alt self.resolve_identifier_in_local_ribs(identifier, - namespace, - span) { + alt self.resolve_identifier_in_local_ribs(identifier, namespace) { some(def) { ret some(def); } @@ -3688,17 +3557,9 @@ class Resolver { // XXX: Merge me with resolve_name_in_module? fn resolve_definition_of_name_in_module(containing_module: @Module, name: Atom, - namespace: Namespace, - xray: XrayFlag) + namespace: Namespace) -> NameDefinition { - if xray == NoXray && !self.name_is_exported(containing_module, name) { - #debug("(resolving definition of name in module) name '%s' is \ - unexported", - *(*self.atom_table).atom_to_str(name)); - ret NoNameDefinition; - } - // First, search children. alt containing_module.children.find(name) { some(child_name_bindings) { @@ -3725,7 +3586,6 @@ class Resolver { alt (*target.bindings).def_for_namespace(namespace) { some(def) { // Found it. - import_resolution.used = true; ret ImportNameDefinition(def); } none { @@ -3769,8 +3629,7 @@ class Resolver { let mut containing_module; alt self.resolve_module_path_for_import(self.current_module, module_path_atoms, - xray, - path.span) { + xray) { Failed { self.session.span_err(path.span, @@ -3792,8 +3651,7 @@ class Resolver { let name = (*self.atom_table).intern(path.idents.last()); alt self.resolve_definition_of_name_in_module(containing_module, name, - namespace, - xray) { + namespace) { NoNameDefinition { // We failed to resolve the name. Report an error. self.session.span_err(path.span, @@ -3823,8 +3681,7 @@ class Resolver { alt self.resolve_module_path_from_root(root_module, module_path_atoms, 0u, - xray, - path.span) { + xray) { Failed { self.session.span_err(path.span, @@ -3846,8 +3703,7 @@ class Resolver { let name = (*self.atom_table).intern(path.idents.last()); alt self.resolve_definition_of_name_in_module(containing_module, name, - namespace, - xray) { + namespace) { NoNameDefinition { // We failed to resolve the name. Report an error. self.session.span_err(path.span, @@ -3865,8 +3721,7 @@ class Resolver { } fn resolve_identifier_in_local_ribs(identifier: ident, - namespace: Namespace, - span: span) + namespace: Namespace) -> option { let name = (*self.atom_table).intern(identifier); @@ -3875,12 +3730,10 @@ class Resolver { let mut search_result; alt namespace { ValueNS { - search_result = self.search_ribs(self.value_ribs, name, span, - DontAllowCapturingSelf); + search_result = self.search_ribs(self.value_ribs, name); } TypeNS { - search_result = self.search_ribs(self.type_ribs, name, span, - AllowCapturingSelf); + search_result = self.search_ribs(self.type_ribs, name); } ModuleNS | ImplNS { fail "module or impl namespaces do not have local ribs"; @@ -4007,83 +3860,6 @@ class Resolver { self.def_map.insert(node_id, def); } - // - // Unused import checking - // - // Although this is a lint pass, it lives in here because it depends on - // resolve data structures. - // - - fn check_for_unused_imports_if_necessary() { - if self.unused_import_lint_level == ignore { - ret; - } - - let root_module = (*self.graph_root).get_module(); - self.check_for_unused_imports_in_module_subtree(root_module); - } - - fn check_for_unused_imports_in_module_subtree(module: @Module) { - // If this isn't a local crate, then bail out. We don't need to check - // for unused imports in external crates. - - alt module.def_id { - some(def_id) if def_id.crate == local_crate { - // OK. Continue. - } - none { - // Check for unused imports in the root module. - } - some(_) { - // Bail out. - #debug("(checking for unused imports in module subtree) not \ - checking for unused imports for '%s'", - self.module_to_str(module)); - ret; - } - } - - self.check_for_unused_imports_in_module(module); - - for module.children.each |_atom, child_name_bindings| { - alt (*child_name_bindings).get_module_if_available() { - none { - // Nothing to do. - } - some(child_module) { - self.check_for_unused_imports_in_module_subtree - (child_module); - } - } - } - - for module.anonymous_children.each |_node_id, child_module| { - self.check_for_unused_imports_in_module_subtree(child_module); - } - } - - fn check_for_unused_imports_in_module(module: @Module) { - for module.import_resolutions.each |_impl_name, import_resolution| { - if !import_resolution.used { - alt self.unused_import_lint_level { - warn { - self.session.span_warn(import_resolution.span, - "unused import"); - } - error { - self.session.span_err(import_resolution.span, - "unused import"); - } - ignore { - self.session.span_bug(import_resolution.span, - "shouldn't be here if lint \ - pass is ignored"); - } - } - } - } - } - // // Diagnostics // diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 56e0c39c56626..fba8d54cfba01 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -166,13 +166,6 @@ export ty_sort_str; export normalize_ty; export to_str; export borrow, serialize_borrow, deserialize_borrow; -export bound_const; -export terr_no_integral_type, terr_ty_param_size, terr_self_substs; -export terr_in_field, terr_record_fields, terr_vstores_differ, terr_arg_count; -export terr_sorts, terr_vec, terr_str, terr_record_size, terr_tuple_size; -export terr_regions_differ, terr_mutability, terr_purity_mismatch; -export terr_constr_mismatch, terr_constr_len, terr_proto_mismatch; -export terr_ret_style_mismatch; // Data types diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 1e198f0f82e2d..aaad7841d4c0c 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -75,8 +75,6 @@ export deserialize_method_map_entry; export vtable_map; export vtable_res; export vtable_origin; -export method_static, method_param, method_trait; -export vtable_static, vtable_param, vtable_trait; #[auto_serialize] enum method_origin { diff --git a/src/rustdoc/config.rs b/src/rustdoc/config.rs index ea397a4398ea5..94d232778679b 100644 --- a/src/rustdoc/config.rs +++ b/src/rustdoc/config.rs @@ -7,8 +7,6 @@ export config; export default_config; export parse_config; export usage; -export markdown, pandoc_html; -export doc_per_crate, doc_per_mod; /// The type of document to output enum output_format { diff --git a/src/test/compile-fail/ambig_impl_2_exe.rs b/src/test/compile-fail/ambig_impl_2_exe.rs index fe7e27dce2316..7cb79ff789e12 100644 --- a/src/test/compile-fail/ambig_impl_2_exe.rs +++ b/src/test/compile-fail/ambig_impl_2_exe.rs @@ -2,6 +2,6 @@ // aux-build:ambig_impl_2_lib.rs use ambig_impl_2_lib; import ambig_impl_2_lib::methods1; -impl methods2 for uint { fn me() -> uint { self } } //~ NOTE is `methods2::me` +impl methods2 for uint { fn me() -> uint { self } } //~ NOTE candidate #2 is `methods2::me` fn main() { 1u.me(); } //~ ERROR multiple applicable methods in scope -//~^ NOTE is `ambig_impl_2_lib::methods1::me` +//~^ NOTE candidate #1 is `ambig_impl_2_lib::methods1::me` diff --git a/src/test/compile-fail/bad-tag-export-2.rs b/src/test/compile-fail/bad-tag-export-2.rs new file mode 100644 index 0000000000000..09018c2167f9a --- /dev/null +++ b/src/test/compile-fail/bad-tag-export-2.rs @@ -0,0 +1,11 @@ +// error-pattern:b does not refer to an enumeration +import bad::*; + +mod bad { + export b::{}; + + fn b() { fail; } +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/bad-tag-export-3.rs b/src/test/compile-fail/bad-tag-export-3.rs new file mode 100644 index 0000000000000..e6934688e21b9 --- /dev/null +++ b/src/test/compile-fail/bad-tag-export-3.rs @@ -0,0 +1,13 @@ +// error-pattern:b does not refer to an enumeration +import bad::*; + +mod bad { + export b::{f, z}; + + fn b() { fail; } + fn f() { fail; } + fn z() { fail; } +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/bad-tag-export-4.rs b/src/test/compile-fail/bad-tag-export-4.rs new file mode 100644 index 0000000000000..fadf0c353a36a --- /dev/null +++ b/src/test/compile-fail/bad-tag-export-4.rs @@ -0,0 +1,12 @@ +// error-pattern:f is not a variant +import bad::*; + +mod bad { + export b::{f, z}; + + enum b { z, k } + fn f() { fail; } +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/bad-tag-export.rs b/src/test/compile-fail/bad-tag-export.rs new file mode 100644 index 0000000000000..fb9d9b8682c6c --- /dev/null +++ b/src/test/compile-fail/bad-tag-export.rs @@ -0,0 +1,14 @@ +// error-pattern:variant e doesn't belong to enum floop +import bad::*; + +mod bad { + + export floop::{a, e}; + + enum floop {a, b, c} + enum bloop {d, e, f} + +} + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/cap-clause-unresolved-copy.rs b/src/test/compile-fail/cap-clause-unresolved-copy.rs index c1ddaff9a9505..97655cb3bd760 100644 --- a/src/test/compile-fail/cap-clause-unresolved-copy.rs +++ b/src/test/compile-fail/cap-clause-unresolved-copy.rs @@ -1,4 +1,4 @@ -// error-pattern:unresolved name +// error-pattern:unresolved name: z fn main() { let x = 5; let y = fn~(copy z, copy x) { diff --git a/src/test/compile-fail/cap-clause-unresolved-move.rs b/src/test/compile-fail/cap-clause-unresolved-move.rs index 5056c2795910c..292b0f430541a 100644 --- a/src/test/compile-fail/cap-clause-unresolved-move.rs +++ b/src/test/compile-fail/cap-clause-unresolved-move.rs @@ -1,4 +1,4 @@ -// error-pattern:unresolved name +// error-pattern:unresolved name: z fn main() { let x = 5; let y = fn~(move z, move x) { diff --git a/src/test/compile-fail/class-implements-bad-iface.rs b/src/test/compile-fail/class-implements-bad-iface.rs index 997a99a94a23d..4e95b986a9ab8 100644 --- a/src/test/compile-fail/class-implements-bad-iface.rs +++ b/src/test/compile-fail/class-implements-bad-iface.rs @@ -1,4 +1,4 @@ -// error-pattern:nonexistent +// error-pattern:unresolved typename: nonexistent class cat : nonexistent { let meows: uint; new(in_x : uint) { self.meows = in_x; } @@ -6,4 +6,4 @@ class cat : nonexistent { fn main() { let nyan = cat(0u); -} +} \ No newline at end of file diff --git a/src/test/compile-fail/class-implements-int.rs b/src/test/compile-fail/class-implements-int.rs index 513b539a45e35..469fee7863b97 100644 --- a/src/test/compile-fail/class-implements-int.rs +++ b/src/test/compile-fail/class-implements-int.rs @@ -1,8 +1,8 @@ -class cat : int { //~ ERROR interface +class cat : int { //~ ERROR can only implement interface types let meows: uint; new(in_x : uint) { self.meows = in_x; } } fn main() { let nyan = cat(0u); -} +} \ No newline at end of file diff --git a/src/test/compile-fail/cross-crate-glob-collision.rs b/src/test/compile-fail/cross-crate-glob-collision.rs new file mode 100644 index 0000000000000..c9694739b493c --- /dev/null +++ b/src/test/compile-fail/cross-crate-glob-collision.rs @@ -0,0 +1,13 @@ +// error-pattern: is glob-imported from multiple different modules +// issue #482 + +use std; +// expecting swap to be defined in vec +import vec::*; +import alternate_supplier::*; + +mod alternate_supplier { + fn contains() { } +} + +fn main() { contains() } diff --git a/src/test/compile-fail/export-import.rs b/src/test/compile-fail/export-import.rs index f010abd6a16e3..f5ed4c4f3a788 100644 --- a/src/test/compile-fail/export-import.rs +++ b/src/test/compile-fail/export-import.rs @@ -1,4 +1,4 @@ -// error-pattern: import +// error-pattern: unresolved import import m::unexported; diff --git a/src/test/compile-fail/iface-test.rs b/src/test/compile-fail/iface-test.rs index bd50cb8d06ccb..1d45832d6df1c 100644 --- a/src/test/compile-fail/iface-test.rs +++ b/src/test/compile-fail/iface-test.rs @@ -1,9 +1,9 @@ iface foo { fn foo(); } -impl of foo for uint {} +impl of foo for uint {} //~ ERROR missing method `foo` -impl of foo for uint { fn foo() -> int {} } +impl of foo for uint { fn foo() -> int {} } //~ ERROR incompatible type -impl of int for uint { fn foo() {} } //~ ERROR interface +impl of int for uint { fn foo() {} } //~ ERROR can only implement interface fn main() {} diff --git a/src/test/compile-fail/import-from-dup.rs b/src/test/compile-fail/import-from-dup.rs new file mode 100644 index 0000000000000..73e14bfc753f6 --- /dev/null +++ b/src/test/compile-fail/import-from-dup.rs @@ -0,0 +1,14 @@ +// error-pattern:duplicate definition of f + +import m1::{f}; +import m2::{f}; + +mod m1 { + fn f() { } +} + +mod m2 { + fn f() { } +} + +fn main() { } diff --git a/src/test/compile-fail/import-from-missing.rs b/src/test/compile-fail/import-from-missing.rs index a22c1ede9abcf..87434a6c5f362 100644 --- a/src/test/compile-fail/import-from-missing.rs +++ b/src/test/compile-fail/import-from-missing.rs @@ -1,4 +1,4 @@ -// error-pattern:unresolved +// error-pattern:unresolved import import spam::{ham, eggs}; mod spam { diff --git a/src/test/compile-fail/import-glob-circular.rs b/src/test/compile-fail/import-glob-circular.rs index 66be16d28a4ec..588b0d5848ee5 100644 --- a/src/test/compile-fail/import-glob-circular.rs +++ b/src/test/compile-fail/import-glob-circular.rs @@ -1,5 +1,5 @@ -// error-pattern: unresolved +// error-pattern: unresolved name mod circ1 { import circ1::*; export f1; diff --git a/src/test/compile-fail/import-glob-multiple.rs b/src/test/compile-fail/import-glob-multiple.rs new file mode 100644 index 0000000000000..83672579130fe --- /dev/null +++ b/src/test/compile-fail/import-glob-multiple.rs @@ -0,0 +1,20 @@ +// error-pattern:common2 + +import mod1::*; +import mod2::*; + +mod mod1 { + fn f1() { #debug("f1"); } + fn common1() { #debug("common") } + fn common2() { #debug("common") } +} + +mod mod2 { + fn f2() { #debug("f1"); } + fn common1() { #debug("common") } + fn common2() { #debug("common") } +} + + + +fn main() { common2(); } diff --git a/src/test/compile-fail/import-loop-2.rs b/src/test/compile-fail/import-loop-2.rs index 7cedd8a9c4166..4040f8333f98e 100644 --- a/src/test/compile-fail/import-loop-2.rs +++ b/src/test/compile-fail/import-loop-2.rs @@ -1,4 +1,4 @@ -// error-pattern:import +// error-pattern:cyclic import mod a { import b::x; diff --git a/src/test/compile-fail/import-loop.rs b/src/test/compile-fail/import-loop.rs index ae1f1a7f36e1c..6aa88db603d7e 100644 --- a/src/test/compile-fail/import-loop.rs +++ b/src/test/compile-fail/import-loop.rs @@ -1,4 +1,4 @@ -// error-pattern:import +// error-pattern: cyclic import import y::x; diff --git a/src/test/compile-fail/import.rs b/src/test/compile-fail/import.rs index 3d3e326f1f5b8..eb47db0725afd 100644 --- a/src/test/compile-fail/import.rs +++ b/src/test/compile-fail/import.rs @@ -1,5 +1,4 @@ -// xfail-test -// error-pattern: unresolved +// error-pattern: unresolved import import zed::bar; import zed::baz; mod zed { diff --git a/src/test/compile-fail/import2.rs b/src/test/compile-fail/import2.rs index d576f66b0641c..6d503a62a4722 100644 --- a/src/test/compile-fail/import2.rs +++ b/src/test/compile-fail/import2.rs @@ -1,4 +1,4 @@ -// error-pattern: unresolved +// error-pattern: unresolved modulename import baz::zed::bar; mod baz { } mod zed { diff --git a/src/test/compile-fail/import3.rs b/src/test/compile-fail/import3.rs index 591ee3afa2711..939c38fed6a4a 100644 --- a/src/test/compile-fail/import3.rs +++ b/src/test/compile-fail/import3.rs @@ -1,4 +1,4 @@ -// error-pattern: unresolved +// error-pattern: unresolved modulename import main::bar; fn main(args: ~[str]) { #debug("foo"); } diff --git a/src/test/compile-fail/import4.rs b/src/test/compile-fail/import4.rs index cf357e8539c78..d4688a4fafdb6 100644 --- a/src/test/compile-fail/import4.rs +++ b/src/test/compile-fail/import4.rs @@ -1,4 +1,4 @@ -// error-pattern: import +// error-pattern: cyclic import mod a { import foo = b::foo; export foo; } mod b { import foo = a::foo; export foo; } diff --git a/src/test/compile-fail/import5.rs b/src/test/compile-fail/import5.rs new file mode 100644 index 0000000000000..85a77411e6828 --- /dev/null +++ b/src/test/compile-fail/import5.rs @@ -0,0 +1,15 @@ +// error-pattern:unresolved import + +mod m1 { + fn foo() { #debug("foo"); } +} + +mod m2 { + import m1::foo; +} + +mod m3 { + import m2::foo; +} + +fn main() { } diff --git a/src/test/compile-fail/issue-1697.rs b/src/test/compile-fail/issue-1697.rs index 4ceaa35fca2c0..63b316813ff98 100644 --- a/src/test/compile-fail/issue-1697.rs +++ b/src/test/compile-fail/issue-1697.rs @@ -1,8 +1,7 @@ -// xfail-test // Testing that we don't fail abnormally after hitting the errors import unresolved::*; //~ ERROR unresolved modulename //~^ ERROR unresolved does not name a module fn main() { -} +} \ No newline at end of file diff --git a/src/test/compile-fail/not-a-pred.rs b/src/test/compile-fail/not-a-pred.rs index 58082feefaab5..0766a8d1feed4 100644 --- a/src/test/compile-fail/not-a-pred.rs +++ b/src/test/compile-fail/not-a-pred.rs @@ -1,5 +1,5 @@ // -*- rust -*- -// error-pattern: lt +// error-pattern: lt is not declared pure fn f(a: int, b: int) : lt(a, b) { } diff --git a/src/test/compile-fail/tag-exports-2.rs b/src/test/compile-fail/tag-exports-2.rs new file mode 100644 index 0000000000000..9472783848846 --- /dev/null +++ b/src/test/compile-fail/tag-exports-2.rs @@ -0,0 +1,18 @@ +// error-pattern:unresolved name: lovejoy +import alder::*; + +mod alder { + export burnside; + export everett::{flanders}; + export irving::{johnson, kearney}; + export marshall::{}; + + enum burnside { couch, davis } + enum everett { flanders, glisan, hoyt } + enum irving { johnson, kearney, lovejoy } + enum marshall { northrup, overton } +} + +fn main() { + let raleigh: irving = lovejoy; +} diff --git a/src/test/compile-fail/tag-exports-3.rs b/src/test/compile-fail/tag-exports-3.rs new file mode 100644 index 0000000000000..e51a0aca115c8 --- /dev/null +++ b/src/test/compile-fail/tag-exports-3.rs @@ -0,0 +1,18 @@ +// error-pattern:unresolved name: northrup +import alder::*; + +mod alder { + export burnside; + export everett::{flanders}; + export irving::{johnson, kearney}; + export marshall::{}; + + enum burnside { couch, davis } + enum everett { flanders, glisan, hoyt } + enum irving { johnson, kearney, lovejoy } + enum marshall { northrup, overton } +} + +fn main() { + let savier: marshall = northrup; +} diff --git a/src/test/compile-fail/tag-exports.rs b/src/test/compile-fail/tag-exports.rs new file mode 100644 index 0000000000000..18965c9184204 --- /dev/null +++ b/src/test/compile-fail/tag-exports.rs @@ -0,0 +1,18 @@ +// error-pattern:unresolved name: glisan +import alder::*; + +mod alder { + export burnside; + export everett::{flanders}; + export irving::{johnson, kearney}; + export marshall::{}; + + enum burnside { couch, davis } + enum everett { flanders, glisan, hoyt } + enum irving { johnson, kearney, lovejoy } + enum marshall { northrup, overton } +} + +fn main() { + let quimby: everett = glisan; +} diff --git a/src/test/run-pass/issue2170exe.rs b/src/test/run-pass/issue2170exe.rs index dafa6b8556a49..34e51843070d5 100644 --- a/src/test/run-pass/issue2170exe.rs +++ b/src/test/run-pass/issue2170exe.rs @@ -3,5 +3,5 @@ use issue2170lib; fn main() { - // let _ = issue2170lib::rsrc(2i32); + let _ = issue2170lib::rsrc(2i32); } diff --git a/src/test/run-pass/tag-exports.rs b/src/test/run-pass/tag-exports.rs index 57937855ca4da..6e5d07299c6d2 100644 --- a/src/test/run-pass/tag-exports.rs +++ b/src/test/run-pass/tag-exports.rs @@ -2,7 +2,6 @@ import alder::*; mod alder { export burnside; - export couch; export everett; export flanders; export irving; From 59355e99ca6e6124dac22cb1700b7a5320984cb8 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 15:08:38 -0700 Subject: [PATCH 19/32] tutioral: Discuss basic function syntax is discussed before the memory model --- doc/tutorial.md | 74 ++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index f0bbde068056a..c8c2de902d175 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -876,6 +876,39 @@ while (x > 10) { x -= 10; } assert x == 10; ~~~~ +# Functions + +Like all other static declarations, such as `type`, functions can be +declared both at the top level and inside other functions (or modules, +which we'll come back to in moment). + +The `ret` keyword immediately returns from a function. It is +optionally followed by an expression to return. In functions that +return `()`, the returned expression can be left off. A function can +also return a value by having its top level block produce an +expression (by omitting the final semicolon). + +Some functions (such as the C function `exit`) never return normally. +In Rust, these are annotated with the pseudo-return type '`!`': + +~~~~ +fn dead_end() -> ! { fail; } +~~~~ + +This helps the compiler avoid spurious error messages. For example, +the following code would be a type error if `dead_end` would be +expected to return. + +~~~~ +# fn can_go_left() -> bool { true } +# fn can_go_right() -> bool { true } +# enum dir { left, right } +# fn dead_end() -> ! { fail; } +let dir = if can_go_left() { left } + else if can_go_right() { right } + else { dead_end(); }; +~~~~ + # The Rust Memory Model At this junction let's take a detour to explain the concepts involved @@ -951,40 +984,7 @@ and the unique pointer (`~T`). These three sigils will appear repeatedly as we explore the language. Learning the appropriate role of each is key to using Rust effectively. -# Functions - -Like all other static declarations, such as `type`, functions can be -declared both at the top level and inside other functions (or modules, -which we'll come back to in moment). - -The `ret` keyword immediately returns from a function. It is -optionally followed by an expression to return. In functions that -return `()`, the returned expression can be left off. A function can -also return a value by having its top level block produce an -expression (by omitting the final semicolon). - -Some functions (such as the C function `exit`) never return normally. -In Rust, these are annotated with the pseudo-return type '`!`': - -~~~~ -fn dead_end() -> ! { fail; } -~~~~ - -This helps the compiler avoid spurious error messages. For example, -the following code would be a type error if `dead_end` would be -expected to return. - -~~~~ -# fn can_go_left() -> bool { true } -# fn can_go_right() -> bool { true } -# enum dir { left, right } -# fn dead_end() -> ! { fail; } -let dir = if can_go_left() { left } - else if can_go_right() { right } - else { dead_end(); }; -~~~~ - -## Closures +# Closures Named functions, like those in the previous section, may not refer to local variables decalared outside the function - they do not @@ -1039,7 +1039,7 @@ position and cannot be stored in structures nor returned from functions. Despite the limitations stack closures are used pervasively in Rust code. -### Boxed closures +## Boxed closures When you need to store a closure in a data structure, a stack closure will not do, since the compiler will refuse to let you store it. For @@ -1081,7 +1081,7 @@ fn mk_appender(suffix: str) -> fn@(str) -> str { } ~~~~ -### Unique closures +## Unique closures Unique closures, written `fn~` in analogy to the `~` pointer type (see next section), hold on to things that can safely be sent between @@ -1090,7 +1090,7 @@ closures, but they also 'own' them—meaning no other code can access them. Unique closures are used in concurrent code, particularly for spawning [tasks](#tasks). -### Closure compatibility +## Closure compatibility A nice property of Rust closures is that you can pass any kind of closure (as long as the arguments and return types match) to functions From 205b483edd713b88b8fb23305fb81dc6d7b52ce5 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 15:09:54 -0700 Subject: [PATCH 20/32] tutorial: Discuss failure and asserts together --- doc/tutorial.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index c8c2de902d175..601a5c336ffe4 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -819,6 +819,21 @@ handle the failure, allowing the program to continue running. to access a vector out of bounds, or running a pattern match with no matching clauses, both result in the equivalent of a `fail`. +## Assertions + +The keyword `assert`, followed by an expression with boolean type, +will check that the given expression results in `true`, and cause a +failure otherwise. It is typically used to double-check things that +*should* hold at a certain point in a program. `assert` statements are +always active; there is no way to build Rust code with assertions +disabled. + +~~~~ +let mut x = 100; +while (x > 10) { x -= 10; } +assert x == 10; +~~~~ + ## Logging Rust has a built-in logging mechanism, using the `log` statement. @@ -861,21 +876,6 @@ and will log the formatted string: Because the macros `#debug`, `#warn`, and `#error` expand to calls to `log`, their arguments are also lazily evaluated. -## Assertions - -The keyword `assert`, followed by an expression with boolean type, -will check that the given expression results in `true`, and cause a -failure otherwise. It is typically used to double-check things that -*should* hold at a certain point in a program. `assert` statements are -always active; there is no way to build Rust code with assertions -disabled. - -~~~~ -let mut x = 100; -while (x > 10) { x -= 10; } -assert x == 10; -~~~~ - # Functions Like all other static declarations, such as `type`, functions can be From 3413b3f5c51284d8f0090bde5da80652fa7366ef Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 15:37:58 -0700 Subject: [PATCH 21/32] tutorial: Expand the section on functions --- doc/tutorial.md | 52 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 601a5c336ffe4..cce8fed220e01 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -880,19 +880,57 @@ their arguments are also lazily evaluated. Like all other static declarations, such as `type`, functions can be declared both at the top level and inside other functions (or modules, -which we'll come back to in moment). +which we'll come back to [later](#modules-and-crates)). -The `ret` keyword immediately returns from a function. It is -optionally followed by an expression to return. In functions that -return `()`, the returned expression can be left off. A function can +We've already seen several function definitions. They are introduced +with the `fn` keyword. The type of arguments are specified following +colons and the return type follows the arrow. + +~~~~ +fn int_to_str(i: int) -> str { + ret "tube sock"; +} +~~~~ + +The `ret` keyword immediately returns from the body of a function. It +is optionally followed by an expression to return. A function can also return a value by having its top level block produce an -expression (by omitting the final semicolon). +expression. + +~~~~ +# const copernicus: int = 0; +fn int_to_str(i: int) -> str { + if i == copernicus { + ret "tube sock"; + } else { + ret "violin"; + } +} +~~~~ + +~~~~ +# const copernicus: int = 0; +fn int_to_str(i: int) -> str { + if i == copernicus { "tube sock" } + else { "violin" } +} +~~~~ + +Functions that do not return a value are said to return nil, `()`, +and both the return type and the return value may be omitted from +the definition. The following two functions are equivalent. + +~~~~ +fn do_nothing_the_hard_way() -> () { ret (); } + +fn do_nothing_the_easy_way() { } +~~~~ Some functions (such as the C function `exit`) never return normally. In Rust, these are annotated with the pseudo-return type '`!`': ~~~~ -fn dead_end() -> ! { fail; } +fn dead_end() -> ! { fail } ~~~~ This helps the compiler avoid spurious error messages. For example, @@ -909,6 +947,8 @@ let dir = if can_go_left() { left } else { dead_end(); }; ~~~~ + + # The Rust Memory Model At this junction let's take a detour to explain the concepts involved From 994c881538f86757f7b1193f7dfa52a67e480805 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 15:51:11 -0700 Subject: [PATCH 22/32] tutorial: Use blockquotes for asides. Add styling --- doc/rust.css | 7 +++++++ doc/tutorial.md | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/doc/rust.css b/doc/rust.css index db45c0e5426b8..d68f0b851c25b 100644 --- a/doc/rust.css +++ b/doc/rust.css @@ -57,3 +57,10 @@ h1.title { background-repeat: no-repeat; background-position: right; } + +blockquote { + color: black; + background-color: lavender; + margin: 1em; + padding: 0.5em 1em 0.5em 1em; +} \ No newline at end of file diff --git a/doc/tutorial.md b/doc/tutorial.md index cce8fed220e01..fc0d3fbec2783 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1553,8 +1553,8 @@ let y = x; // Copy the pointer, increase refcount // When x and y go out of scope, refcount goes to 0, box is freed ~~~~ -***Note:*** We will in the future switch to garbage collection, rather -than reference counting, for shared boxes. +> ***Note:*** We will in the future switch to garbage collection, +> rather than reference counting, for shared boxes. Shared boxes never cross task boundaries. @@ -1642,8 +1642,8 @@ Rust vectors are always heap-allocated and unique. A value of type `~[T]` is represented by a pointer to a section of heap memory containing any number of values of type `T`. -***Note:*** This uniqueness is turning out to be quite awkward in -practice, and might change in the future. +> ***Note:*** This uniqueness is turning out to be quite awkward in +> practice, and might change in the future. Vector literals are enclosed in square brackets. Dereferencing is done with square brackets (zero-based): @@ -1676,10 +1676,10 @@ logarithmically, so the above code generates about the same amount of copying and reallocation as `push` implementations in most other languages. -***Note:*** Actually, currently, growing a vector is *exactly* as -inefficient as it looks, since vector `+` has been moved to the -libraries and Rust's operator overloading support is insufficient to -allow this optimization. Try using `vec::push`. +> ***Note:*** Actually, currently, growing a vector is *exactly* as +> inefficient as it looks, since vector `+` has been moved to the +> libraries and Rust's operator overloading support is insufficient to +> allow this optimization. Try using `vec::push`. ## Strings @@ -1946,8 +1946,8 @@ fn plus1(&&x: int) -> int { x + 1 } vec::map(~[1, 2, 3], plus1); ~~~~ -***Note:***: This is inconvenient, and we are hoping to get rid of this -restriction in the future. +> ***Note:*** This is inconvenient, and we are hoping to get rid of +> this restriction in the future. # Modules and crates @@ -2222,8 +2222,8 @@ object-oriented languages tend to solve with methods and inheritance. For example, writing a function that can operate on multiple types of collections. -***Note:***: This feature is very new, and will need a few extensions to be -applicable to more advanced use cases. +> ***Note:*** This feature is very new, and will need a few extensions to be +> applicable to more advanced use cases. ## Declaration @@ -2663,8 +2663,8 @@ copying it by making use of [unique boxes](#unique-boxes), which allow the sending task to release ownership of a value, so that the receiving task can keep on using it. -***Note:***: As Rust evolves, we expect the task API to grow and change -somewhat. The tutorial documents the API as it exists today. +> ***Note:*** As Rust evolves, we expect the task API to grow and +> change somewhat. The tutorial documents the API as it exists today. ## Spawning a task From 71966176691588623b9f40d96879e4024774b215 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 16:23:10 -0700 Subject: [PATCH 23/32] tutorial: Reorder sections around the memory model discussion Also some minor tweaks. --- doc/tutorial.md | 870 +++++++++++++++++++++++------------------------- 1 file changed, 425 insertions(+), 445 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index fc0d3fbec2783..d3bbb869e6e90 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -883,7 +883,7 @@ declared both at the top level and inside other functions (or modules, which we'll come back to [later](#modules-and-crates)). We've already seen several function definitions. They are introduced -with the `fn` keyword. The type of arguments are specified following +with the `fn` keyword, the type of arguments are specified following colons and the return type follows the arrow. ~~~~ @@ -947,7 +947,211 @@ let dir = if can_go_left() { left } else { dead_end(); }; ~~~~ +# Basic datatypes +The core datatypes of Rust are structural records, enums (tagged +unions, algebraic data types), and tuples. They are immutable +by default. + +~~~~ +type point = {x: float, y: float}; + +enum shape { + circle(point, float), + rectangle(point, point) +} +~~~~ + +## Records + +Rust record types are written `{field1: T1, field2: T2 [, ...]}`, +where `T1`, `T2`, ... denote types. Record literals are written in +the same way, but with expressions instead of types. They are quite +similar to C structs, and even laid out the same way in memory (so you +can read from a Rust struct in C, and vice-versa). The dot operator is +used to access record fields (`mypoint.x`). + +Fields that you want to mutate must be explicitly marked `mut`. + +~~~~ +type stack = {content: ~[int], mut head: uint}; +~~~~ + +With such a type, you can do `mystack.head += 1u`. If `mut` were +omitted from the type, such an assignment would result in a type +error. + +To create a new record based on the value of an existing record +you construct it using the `with` keyword: + +~~~~ +let oldpoint = {x: 10f, y: 20f}; +let newpoint = {x: 0f with oldpoint}; +assert newpoint == {x: 0f, y: 20f}; +~~~~ + +This will create a new record, copying all the fields from `oldpoint` +into it, except for the ones that are explicitly set in the literal. + +Rust record types are *structural*. This means that `{x: float, y: +float}` is not just a way to define a new type, but is the actual name +of the type. Record types can be used without first defining them. If +module A defines `type point = {x: float, y: float}`, and module B, +without knowing anything about A, defines a function that returns an +`{x: float, y: float}`, you can use that return value as a `point` in +module A. (Remember that `type` defines an additional name for a type, +not an actual new type.) + +## Record patterns + +Records can be destructured in `alt` patterns. The basic syntax is +`{fieldname: pattern, ...}`, but the pattern for a field can be +omitted as a shorthand for simply binding the variable with the same +name as the field. + +~~~~ +# let mypoint = {x: 0f, y: 0f}; +alt mypoint { + {x: 0f, y: y_name} { /* Provide sub-patterns for fields */ } + {x, y} { /* Simply bind the fields */ } +} +~~~~ + +The field names of a record do not have to appear in a pattern in the +same order they appear in the type. When you are not interested in all +the fields of a record, a record pattern may end with `, _` (as in +`{field1, _}`) to indicate that you're ignoring all other fields. + +## Enums + +Enums are datatypes that have several alternate representations. For +example, consider the type shown earlier: + +~~~~ +# type point = {x: float, y: float}; +enum shape { + circle(point, float), + rectangle(point, point) +} +~~~~ + +A value of this type is either a circle, in which case it contains a +point record and a float, or a rectangle, in which case it contains +two point records. The run-time representation of such a value +includes an identifier of the actual form that it holds, much like the +'tagged union' pattern in C, but with better ergonomics. + +The above declaration will define a type `shape` that can be used to +refer to such shapes, and two functions, `circle` and `rectangle`, +which can be used to construct values of the type (taking arguments of +the specified types). So `circle({x: 0f, y: 0f}, 10f)` is the way to +create a new circle. + +Enum variants need not have type parameters. This, for example, is +equivalent to a C enum: + +~~~~ +enum direction { + north, + east, + south, + west +} +~~~~ + +This will define `north`, `east`, `south`, and `west` as constants, +all of which have type `direction`. + +When an enum is C-like, that is, when none of the variants have +parameters, it is possible to explicitly set the discriminator values +to an integer value: + +~~~~ +enum color { + red = 0xff0000, + green = 0x00ff00, + blue = 0x0000ff +} +~~~~ + +If an explicit discriminator is not specified for a variant, the value +defaults to the value of the previous variant plus one. If the first +variant does not have a discriminator, it defaults to 0. For example, +the value of `north` is 0, `east` is 1, etc. + +When an enum is C-like the `as` cast operator can be used to get the +discriminator's value. + + + +There is a special case for enums with a single variant. These are +used to define new types in such a way that the new name is not just a +synonym for an existing type, but its own distinct type. If you say: + +~~~~ +enum gizmo_id = int; +~~~~ + +That is a shorthand for this: + +~~~~ +enum gizmo_id { gizmo_id(int) } +~~~~ + +Enum types like this can have their content extracted with the +dereference (`*`) unary operator: + +~~~~ +# enum gizmo_id = int; +let my_gizmo_id = gizmo_id(10); +let id_int: int = *my_gizmo_id; +~~~~ + +## Enum patterns + +For enum types with multiple variants, destructuring is the only way to +get at their contents. All variant constructors can be used as +patterns, as in this definition of `area`: + +~~~~ +# type point = {x: float, y: float}; +# enum shape { circle(point, float), rectangle(point, point) } +fn area(sh: shape) -> float { + alt sh { + circle(_, size) { float::consts::pi * size * size } + rectangle({x, y}, {x: x2, y: y2}) { (x2 - x) * (y2 - y) } + } +} +~~~~ + +Another example, matching nullary enum variants: + +~~~~ +# type point = {x: float, y: float}; +# enum direction { north, east, south, west } +fn point_from_direction(dir: direction) -> point { + alt dir { + north { {x: 0f, y: 1f} } + east { {x: 1f, y: 0f} } + south { {x: 0f, y: -1f} } + west { {x: -1f, y: 0f} } + } +} +~~~~ + +## Tuples + +Tuples in Rust behave exactly like records, except that their fields +do not have names (and can thus not be accessed with dot notation). +Tuples can have any arity except for 0 or 1 (though you may consider +nil, `()`, as the empty tuple if you like). + +~~~~ +let mytup: (int, int, float) = (10, 20, 30.0); +alt mytup { + (a, b, c) { log(info, a + b + (c as int)); } +} +~~~~ # The Rust Memory Model @@ -1024,42 +1228,236 @@ and the unique pointer (`~T`). These three sigils will appear repeatedly as we explore the language. Learning the appropriate role of each is key to using Rust effectively. -# Closures +# Pointers -Named functions, like those in the previous section, may not refer -to local variables decalared outside the function - they do not -close over their environment. For example you couldn't write the -following: +In contrast to a lot of modern languages, record and enum types in +Rust are not represented as pointers to allocated memory. They are, +like in C and C++, represented directly. This means that if you `let x += {x: 1f, y: 1f};`, you are creating a record on the stack. If you +then copy it into a data structure, the whole record is copied, not +just a pointer. -~~~~ {.ignore} -let foo = 10; +For small records like `point`, this is usually more efficient than +allocating memory and going through a pointer. But for big records, or +records with mutable fields, it can be useful to have a single copy on +the heap, and refer to that through a pointer. -fn bar() -> int { - ret foo; // `bar` cannot refer to `foo` -} -~~~~ +Rust supports several types of pointers. The safe pointer types are +`@T` for shared boxes allocated on the local heap, `~T`, for +uniquely-owned boxes allocated on the exchange heap, and `&T`, for +borrowed pointers, which may point to any memory, and whose lifetimes +are governed by the call stack. -Rust also supports _closures_, functions that can access variables in -the enclosing scope. +Rust also has an unsafe pointer, written `*T`, which is a completely +unchecked pointer type only used in unsafe code (and thus, in typical +Rust code, very rarely). -~~~~ -# import println = io::println; -fn call_closure_with_ten(b: fn(int)) { b(10); } +All pointer types can be dereferenced with the `*` unary operator. -let captured_var = 20; -let closure = |arg| println(#fmt("captured_var=%d, arg=%d", captured_var, arg)); +## Shared boxes -call_closure_with_ten(closure); -~~~~ +Shared boxes are pointers to heap-allocated, reference counted memory. +A cycle collector ensures that circular references do not result in +memory leaks. -The types of the arguments are generally omitted, as is the return -type, because the compiler can almost always infer them. In the rare -case where the compiler needs assistance though, the arguments and -return types may be annotated. +Creating a shared box is done by simply applying the unary `@` +operator to an expression. The result of the expression will be boxed, +resulting in a box of the right type. For example: ~~~~ -# type mygoodness = fn(str) -> str; type what_the = int; -let bloop = |well, oh: mygoodness| -> what_the { fail oh(well) }; +let x = @10; // New box, refcount of 1 +let y = x; // Copy the pointer, increase refcount +// When x and y go out of scope, refcount goes to 0, box is freed +~~~~ + +> ***Note:*** We will in the future switch to garbage collection, +> rather than reference counting, for shared boxes. + +Shared boxes never cross task boundaries. + +## Unique boxes + +In contrast to shared boxes, unique boxes have a single owner and thus +two unique boxes may not refer to the same memory. All unique boxes +across all tasks are allocated on a single _exchange heap_, where +their uniquely owned nature allows them to be passed between tasks. + +Because unique boxes are uniquely owned, copying them involves allocating +a new unique box and duplicating the contents. Copying unique boxes +is expensive so the compiler will complain if you do. + +~~~~ +let x = ~10; +let y = x; // error: copying a non-implicitly copyable type +~~~~ + +If you really want to copy a unique box you must say so explicitly. + +~~~~ +let x = ~10; +let y = copy x; +~~~~ + +This is where the 'move' (`<-`) operator comes in. It is similar to +`=`, but it de-initializes its source. Thus, the unique box can move +from `x` to `y`, without violating the constraint that it only has a +single owner (if you used assignment instead of the move operator, the +box would, in principle, be copied). + +~~~~ +let x = ~10; +let y <- x; +~~~~ + +Unique boxes, when they do not contain any shared boxes, can be sent +to other tasks. The sending task will give up ownership of the box, +and won't be able to access it afterwards. The receiving task will +become the sole owner of the box. + +## Borrowed pointers + +Rust borrowed pointers are a general purpose reference/pointer type, +similar to the C++ reference type, but guaranteed to point to valid +memory. In contrast to unique pointers, where the holder of a unique +pointer is the owner of the pointed-to memory, borrowed pointers never +imply ownership. Pointers may be borrowed from any type, in which case +the pointer is guaranteed not to outlive the value it points to. + +~~~~ +# fn work_with_foo_by_pointer(f: &str) { } +let foo = "foo"; +work_with_foo_by_pointer(&foo); +~~~~ + +The following shows an example of what is _not_ possible with borrowed +pointers. If you were able to write this then the pointer to `foo` +would outlive `foo` itself. + +~~~~ {.ignore} +let foo_ptr; +{ + let foo = "foo"; + foo_ptr = &foo; +} +~~~~ + +## Mutability + +All pointer types have a mutable variant, written `@mut T` or `~mut +T`. Given such a pointer, you can write to its contents by combining +the dereference operator with a mutating action. + +~~~~ +fn increase_contents(pt: @mut int) { + *pt += 1; +} +~~~~ + +# Vectors + +Rust vectors are always heap-allocated and unique. A value of type +`~[T]` is represented by a pointer to a section of heap memory +containing any number of values of type `T`. + +> ***Note:*** This uniqueness is turning out to be quite awkward in +> practice, and might change in the future. + +Vector literals are enclosed in square brackets. Dereferencing is done +with square brackets (zero-based): + +~~~~ +let myvec = ~[true, false, true, false]; +if myvec[1] { io::println("boom"); } +~~~~ + +By default, vectors are immutable—you can not replace their elements. +The type written as `~[mut T]` is a vector with mutable +elements. Mutable vector literals are written `~[mut]` (empty) or `~[mut +1, 2, 3]` (with elements). + +The `+` operator means concatenation when applied to vector types. +Growing a vector in Rust is not as inefficient as it looks : + +~~~~ +let mut myvec = ~[], i = 0; +while i < 100 { + myvec += ~[i]; + i += 1; +} +~~~~ + +Because a vector is unique, replacing it with a longer one (which is +what `+= ~[i]` does) is indistinguishable from appending to it +in-place. Vector representations are optimized to grow +logarithmically, so the above code generates about the same amount of +copying and reallocation as `push` implementations in most other +languages. + +> ***Note:*** Actually, currently, growing a vector is *exactly* as +> inefficient as it looks, since vector `+` has been moved to the +> libraries and Rust's operator overloading support is insufficient to +> allow this optimization. Try using `vec::push`. + +## Strings + +The `str` type in Rust is represented exactly the same way as a vector +of bytes (`~[u8]`), except that it is guaranteed to have a trailing +null byte (for interoperability with C APIs). + +This sequence of bytes is interpreted as an UTF-8 encoded sequence of +characters. This has the advantage that UTF-8 encoded I/O (which +should really be the default for modern systems) is very fast, and +that strings have, for most intents and purposes, a nicely compact +representation. It has the disadvantage that you only get +constant-time access by byte, not by character. + +A lot of algorithms don't need constant-time indexed access (they +iterate over all characters, which `str::chars` helps with), and +for those that do, many don't need actual characters, and can operate +on bytes. For algorithms that do really need to index by character, +there's the option to convert your string to a character vector (using +`str::chars`). + +Like vectors, strings are always unique. You can wrap them in a shared +box to share them. Unlike vectors, there is no mutable variant of +strings. They are always immutable. + +# Closures + +Named functions, like those in the previous section, may not refer +to local variables decalared outside the function - they do not +close over their environment. For example you couldn't write the +following: + +~~~~ {.ignore} +let foo = 10; + +fn bar() -> int { + ret foo; // `bar` cannot refer to `foo` +} +~~~~ + +Rust also supports _closures_, functions that can access variables in +the enclosing scope. + +~~~~ +# import println = io::println; +fn call_closure_with_ten(b: fn(int)) { b(10); } + +let captured_var = 20; +let closure = |arg| println(#fmt("captured_var=%d, arg=%d", captured_var, arg)); + +call_closure_with_ten(closure); +~~~~ + +The types of the arguments are generally omitted, as is the return +type, because the compiler can almost always infer them. In the rare +case where the compiler needs assistance though, the arguments and +return types may be annotated. + +~~~~ +# type mygoodness = fn(str) -> str; type what_the = int; +let bloop = |well, oh: mygoodness| -> what_the { fail oh(well) }; ~~~~ There are several forms of closure, each with its own role. The most @@ -1289,424 +1687,6 @@ fn contains(v: ~[int], elt: int) -> bool { `for` syntax only works with stack closures. - -# Datatypes - -The core datatypes of Rust are structural records, enums (tagged -unions, algebraic data types), and classes. They are immutable -by default. - -~~~~ -type point = {x: float, y: float}; - -enum shape { - circle(point, float), - rectangle(point, point) -} - -class drawing { - let mut shapes: [shape]; - - new() { - self.shapes = []; - } - - fn add_shape(new_shape: shape) { - self.shapes += [new_shape]; - } -} - -let my_drawing = drawing(); -my_drawing.add_shape(circle({x: 0.0, y: 0.0}, 10.0)); -~~~~ - -## Records - -Rust record types are written `{field1: T1, field2: T2 [, ...]}`, -where `T1`, `T2`, ... denote types. Record literals are written in -the same way, but with expressions instead of types. They are quite -similar to C structs, and even laid out the same way in memory (so you -can read from a Rust struct in C, and vice-versa). The dot operator is -used to access record fields (`mypoint.x`). - -Fields that you want to mutate must be explicitly marked `mut`. - -~~~~ -type stack = {content: ~[int], mut head: uint}; -~~~~ - -With such a type, you can do `mystack.head += 1u`. If `mut` were -omitted from the type, such an assignment would result in a type -error. - -To create a new record based on the value of an existing record -you construct it using the `with` keyword: - -~~~~ -let oldpoint = {x: 10f, y: 20f}; -let newpoint = {x: 0f with oldpoint}; -assert newpoint == {x: 0f, y: 20f}; -~~~~ - -This will create a new record, copying all the fields from `oldpoint` -into it, except for the ones that are explicitly set in the literal. - -Rust record types are *structural*. This means that `{x: float, y: -float}` is not just a way to define a new type, but is the actual name -of the type. Record types can be used without first defining them. If -module A defines `type point = {x: float, y: float}`, and module B, -without knowing anything about A, defines a function that returns an -`{x: float, y: float}`, you can use that return value as a `point` in -module A. (Remember that `type` defines an additional name for a type, -not an actual new type.) - -## Record patterns - -Records can be destructured in `alt` patterns. The basic syntax is -`{fieldname: pattern, ...}`, but the pattern for a field can be -omitted as a shorthand for simply binding the variable with the same -name as the field. - -~~~~ -# let mypoint = {x: 0f, y: 0f}; -alt mypoint { - {x: 0f, y: y_name} { /* Provide sub-patterns for fields */ } - {x, y} { /* Simply bind the fields */ } -} -~~~~ - -The field names of a record do not have to appear in a pattern in the -same order they appear in the type. When you are not interested in all -the fields of a record, a record pattern may end with `, _` (as in -`{field1, _}`) to indicate that you're ignoring all other fields. - -## Enums - -Enums are datatypes that have several alternate representations. For -example, consider the type shown earlier: - -~~~~ -# type point = {x: float, y: float}; -enum shape { - circle(point, float), - rectangle(point, point) -} -~~~~ - -A value of this type is either a circle, in which case it contains a -point record and a float, or a rectangle, in which case it contains -two point records. The run-time representation of such a value -includes an identifier of the actual form that it holds, much like the -'tagged union' pattern in C, but with better ergonomics. - -The above declaration will define a type `shape` that can be used to -refer to such shapes, and two functions, `circle` and `rectangle`, -which can be used to construct values of the type (taking arguments of -the specified types). So `circle({x: 0f, y: 0f}, 10f)` is the way to -create a new circle. - -Enum variants need not have type parameters. This, for example, is -equivalent to a C enum: - -~~~~ -enum direction { - north, - east, - south, - west -} -~~~~ - -This will define `north`, `east`, `south`, and `west` as constants, -all of which have type `direction`. - -When an enum is C-like, that is, when none of the variants have -parameters, it is possible to explicitly set the discriminator values -to an integer value: - -~~~~ -enum color { - red = 0xff0000, - green = 0x00ff00, - blue = 0x0000ff -} -~~~~ - -If an explicit discriminator is not specified for a variant, the value -defaults to the value of the previous variant plus one. If the first -variant does not have a discriminator, it defaults to 0. For example, -the value of `north` is 0, `east` is 1, etc. - -When an enum is C-like the `as` cast operator can be used to get the -discriminator's value. - - - -There is a special case for enums with a single variant. These are -used to define new types in such a way that the new name is not just a -synonym for an existing type, but its own distinct type. If you say: - -~~~~ -enum gizmo_id = int; -~~~~ - -That is a shorthand for this: - -~~~~ -enum gizmo_id { gizmo_id(int) } -~~~~ - -Enum types like this can have their content extracted with the -dereference (`*`) unary operator: - -~~~~ -# enum gizmo_id = int; -let my_gizmo_id = gizmo_id(10); -let id_int: int = *my_gizmo_id; -~~~~ - -## Enum patterns - -For enum types with multiple variants, destructuring is the only way to -get at their contents. All variant constructors can be used as -patterns, as in this definition of `area`: - -~~~~ -# type point = {x: float, y: float}; -# enum shape { circle(point, float), rectangle(point, point) } -fn area(sh: shape) -> float { - alt sh { - circle(_, size) { float::consts::pi * size * size } - rectangle({x, y}, {x: x2, y: y2}) { (x2 - x) * (y2 - y) } - } -} -~~~~ - -Another example, matching nullary enum variants: - -~~~~ -# type point = {x: float, y: float}; -# enum direction { north, east, south, west } -fn point_from_direction(dir: direction) -> point { - alt dir { - north { {x: 0f, y: 1f} } - east { {x: 1f, y: 0f} } - south { {x: 0f, y: -1f} } - west { {x: -1f, y: 0f} } - } -} -~~~~ - -## Tuples - -Tuples in Rust behave exactly like records, except that their fields -do not have names (and can thus not be accessed with dot notation). -Tuples can have any arity except for 0 or 1 (though you may consider -nil, `()`, as the empty tuple if you like). - -~~~~ -let mytup: (int, int, float) = (10, 20, 30.0); -alt mytup { - (a, b, c) { log(info, a + b + (c as int)); } -} -~~~~ - -## Pointers - -In contrast to a lot of modern languages, record and enum types in -Rust are not represented as pointers to allocated memory. They are, -like in C and C++, represented directly. This means that if you `let x -= {x: 1f, y: 1f};`, you are creating a record on the stack. If you -then copy it into a data structure, the whole record is copied, not -just a pointer. - -For small records like `point`, this is usually more efficient than -allocating memory and going through a pointer. But for big records, or -records with mutable fields, it can be useful to have a single copy on -the heap, and refer to that through a pointer. - -Rust supports several types of pointers. The safe pointer types are -`@T` for shared boxes allocated on the local heap, `~T`, for -uniquely-owned boxes allocated on the exchange heap, and `&T`, for -borrowed pointers, which may point to any memory, and whose lifetimes -are governed by the call stack. - -Rust also has an unsafe pointer, written `*T`, which is a completely -unchecked pointer type only used in unsafe code (and thus, in typical -Rust code, very rarely). - -All pointer types can be dereferenced with the `*` unary operator. - -### Shared boxes - -Shared boxes are pointers to heap-allocated, reference counted memory. -A cycle collector ensures that circular references do not result in -memory leaks. - -Creating a shared box is done by simply applying the unary `@` -operator to an expression. The result of the expression will be boxed, -resulting in a box of the right type. For example: - -~~~~ -let x = @10; // New box, refcount of 1 -let y = x; // Copy the pointer, increase refcount -// When x and y go out of scope, refcount goes to 0, box is freed -~~~~ - -> ***Note:*** We will in the future switch to garbage collection, -> rather than reference counting, for shared boxes. - -Shared boxes never cross task boundaries. - -### Unique boxes - -In contrast to shared boxes, unique boxes have a single owner and thus -two unique boxes may not refer to the same memory. All unique boxes -across all tasks are allocated on a single _exchange heap_, where -their uniquely owned nature allows them to be passed between tasks. - -Because unique boxes are uniquely owned, copying them involves allocating -a new unique box and duplicating the contents. Copying unique boxes -is expensive so the compiler will complain if you do. - -~~~~ -let x = ~10; -let y = x; // error: copying a non-implicitly copyable type -~~~~ - -If you really want to copy a unique box you must say so explicitly. - -~~~~ -let x = ~10; -let y = copy x; -~~~~ - -This is where the 'move' (`<-`) operator comes in. It is similar to -`=`, but it de-initializes its source. Thus, the unique box can move -from `x` to `y`, without violating the constraint that it only has a -single owner (if you used assignment instead of the move operator, the -box would, in principle, be copied). - -~~~~ -let x = ~10; -let y <- x; -~~~~ - -Unique boxes, when they do not contain any shared boxes, can be sent -to other tasks. The sending task will give up ownership of the box, -and won't be able to access it afterwards. The receiving task will -become the sole owner of the box. - -### Borrowed pointers - -Rust borrowed pointers are a general purpose reference/pointer type, -similar to the C++ reference type, but guaranteed to point to valid -memory. In contrast to unique pointers, where the holder of a unique -pointer is the owner of the pointed-to memory, borrowed pointers never -imply ownership. Pointers may be borrowed from any type, in which case -the pointer is guaranteed not to outlive the value it points to. - -~~~~ -# fn work_with_foo_by_pointer(f: &str) { } -let foo = "foo"; -work_with_foo_by_pointer(&foo); -~~~~ - -The following shows an example of what is _not_ possible with borrowed -pointers. If you were able to write this then the pointer to `foo` -would outlive `foo` itself. - -~~~~ {.ignore} -let foo_ptr; -{ - let foo = "foo"; - foo_ptr = &foo; -} -~~~~ - -### Mutability - -All pointer types have a mutable variant, written `@mut T` or `~mut -T`. Given such a pointer, you can write to its contents by combining -the dereference operator with a mutating action. - -~~~~ -fn increase_contents(pt: @mut int) { - *pt += 1; -} -~~~~ - -## Vectors - -Rust vectors are always heap-allocated and unique. A value of type -`~[T]` is represented by a pointer to a section of heap memory -containing any number of values of type `T`. - -> ***Note:*** This uniqueness is turning out to be quite awkward in -> practice, and might change in the future. - -Vector literals are enclosed in square brackets. Dereferencing is done -with square brackets (zero-based): - -~~~~ -let myvec = ~[true, false, true, false]; -if myvec[1] { io::println("boom"); } -~~~~ - -By default, vectors are immutable—you can not replace their elements. -The type written as `~[mut T]` is a vector with mutable -elements. Mutable vector literals are written `~[mut]` (empty) or `~[mut -1, 2, 3]` (with elements). - -The `+` operator means concatenation when applied to vector types. -Growing a vector in Rust is not as inefficient as it looks : - -~~~~ -let mut myvec = ~[], i = 0; -while i < 100 { - myvec += ~[i]; - i += 1; -} -~~~~ - -Because a vector is unique, replacing it with a longer one (which is -what `+= ~[i]` does) is indistinguishable from appending to it -in-place. Vector representations are optimized to grow -logarithmically, so the above code generates about the same amount of -copying and reallocation as `push` implementations in most other -languages. - -> ***Note:*** Actually, currently, growing a vector is *exactly* as -> inefficient as it looks, since vector `+` has been moved to the -> libraries and Rust's operator overloading support is insufficient to -> allow this optimization. Try using `vec::push`. - -## Strings - -The `str` type in Rust is represented exactly the same way as a vector -of bytes (`~[u8]`), except that it is guaranteed to have a trailing -null byte (for interoperability with C APIs). - -This sequence of bytes is interpreted as an UTF-8 encoded sequence of -characters. This has the advantage that UTF-8 encoded I/O (which -should really be the default for modern systems) is very fast, and -that strings have, for most intents and purposes, a nicely compact -representation. It has the disadvantage that you only get -constant-time access by byte, not by character. - -A lot of algorithms don't need constant-time indexed access (they -iterate over all characters, which `str::chars` helps with), and -for those that do, many don't need actual characters, and can operate -on bytes. For algorithms that do really need to index by character, -there's the option to convert your string to a character vector (using -`str::chars`). - -Like vectors, strings are always unique. You can wrap them in a shared -box to share them. Unlike vectors, there is no mutable variant of -strings. They are always immutable. - -NOTE: Section on resources removed. ToDo: document classes and destructors - # Argument passing Rust datatypes are not trivial to copy (the way, for example, From 04feb6e1ab50bfe819ecc383e9e8712efa2b93af Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 16:27:59 -0700 Subject: [PATCH 24/32] tutorial: Add a note about the experimentalness of borrowed pointers --- doc/tutorial.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/tutorial.md b/doc/tutorial.md index d3bbb869e6e90..cb63c41d1e38b 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1341,6 +1341,12 @@ let foo_ptr; } ~~~~ +> ***Note:*** borrowed pointers are a new addition to the language. +> They are not used extensively yet but are expected to become the +> pointer type used in many common situations, in particular for +> by-reference argument passing. Rust's current solution for passing +> arguments by reference is [argument modes](#argument-passing). + ## Mutability All pointer types have a mutable variant, written `@mut T` or `~mut From 6fa64eeb20fad30ec327c0cd3931079e30696228 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 16:49:51 -0700 Subject: [PATCH 25/32] tutorial: Pointer cleanup --- doc/tutorial.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index cb63c41d1e38b..5150b57b9cca2 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1228,14 +1228,14 @@ and the unique pointer (`~T`). These three sigils will appear repeatedly as we explore the language. Learning the appropriate role of each is key to using Rust effectively. -# Pointers +# Boxes and pointers -In contrast to a lot of modern languages, record and enum types in -Rust are not represented as pointers to allocated memory. They are, -like in C and C++, represented directly. This means that if you `let x -= {x: 1f, y: 1f};`, you are creating a record on the stack. If you -then copy it into a data structure, the whole record is copied, not -just a pointer. +In contrast to a lot of modern languages, aggregate types like records +and enums are not represented as pointers to allocated memory. They +are, like in C and C++, represented directly. This means that if you +`let x = {x: 1f, y: 1f};`, you are creating a record on the stack. If +you then copy it into a data structure, the whole record is copied, +not just a pointer. For small records like `point`, this is usually more efficient than allocating memory and going through a pointer. But for big records, or @@ -1260,19 +1260,21 @@ Shared boxes are pointers to heap-allocated, reference counted memory. A cycle collector ensures that circular references do not result in memory leaks. +> ***Note:*** We will in the future switch to garbage collection, +> rather than reference counting, for shared boxes. + Creating a shared box is done by simply applying the unary `@` operator to an expression. The result of the expression will be boxed, -resulting in a box of the right type. For example: +resulting in a box of the right type. Copying a shared box, as happens +during assignment, only copies a pointer, never the contents of the +box. ~~~~ -let x = @10; // New box, refcount of 1 +let x: @int = @10; // New box, refcount of 1 let y = x; // Copy the pointer, increase refcount // When x and y go out of scope, refcount goes to 0, box is freed ~~~~ -> ***Note:*** We will in the future switch to garbage collection, -> rather than reference counting, for shared boxes. - Shared boxes never cross task boundaries. ## Unique boxes @@ -1309,6 +1311,11 @@ let x = ~10; let y <- x; ~~~~ +> ***Note:*** this discussion of copying vs moving does not account +> for the "last use" rules that automatically promote copy operations +> to moves. This is an evolving area of the language that will +> continue to change. + Unique boxes, when they do not contain any shared boxes, can be sent to other tasks. The sending task will give up ownership of the box, and won't be able to access it afterwards. The receiving task will From f46e4ba1fba82137500db9ab2d95e5c8bfb4a8bf Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 17:31:39 -0700 Subject: [PATCH 26/32] tutorial: Expand the section on vectors --- doc/tutorial.md | 120 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 81 insertions(+), 39 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 5150b57b9cca2..b8eb9fdc6e878 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1368,19 +1368,46 @@ fn increase_contents(pt: @mut int) { # Vectors -Rust vectors are always heap-allocated and unique. A value of type -`~[T]` is represented by a pointer to a section of heap memory -containing any number of values of type `T`. +Vectors represent a section of memory that contains some number +of values. Like other types in Rust, vectors can be stored on +the stack, the local heap, or the exchange heap. -> ***Note:*** This uniqueness is turning out to be quite awkward in -> practice, and might change in the future. +~~~ +enum crayon { + almond, antique_brass, apricot, + aquamarine, asparagus, atomic_tangerine, + banana_mania, beaver, bittersweet +} + +// A stack vector of crayons +let stack_crayons: &[crayon] = &[almond, antique_brass, apricot]; +// A local heap (shared) vector of crayons +let local_crayons: @[crayon] = @[aquamarine, asparagus, atomic_tangerine]; +// An exchange heap (unique) vector of crayons +let exchange_crayons: ~[crayon] = ~[banana_mania, beaver, bittersweet]; +~~~ + +> ***Note:*** Until recently Rust only had unique vectors, using the +> unadorned `[]` syntax for literals. This syntax is still supported +> but is deprecated. In the future it will probably represent some +> "reasonable default" vector type. +> +> Unique vectors are the currently-recomended vector type for general +> use as they are the most tested and well-supported by existing +> libraries. There will be a gradual shift toward using more +> stack and local vectors in the coming releases. -Vector literals are enclosed in square brackets. Dereferencing is done -with square brackets (zero-based): +Vector literals are enclosed in square brackets and dereferencing is +also done with square brackets (zero-based): ~~~~ -let myvec = ~[true, false, true, false]; -if myvec[1] { io::println("boom"); } +# enum crayon { almond, antique_brass, apricot, +# aquamarine, asparagus, atomic_tangerine, +# banana_mania, beaver, bittersweet }; +# fn draw_crying_puppy(c: crayon) { } + +let crayons = ~[banana_mania, beaver, bittersweet]; +if crayons[0] == bittersweet { draw_crying_puppy(crayons[0]); } ~~~~ By default, vectors are immutable—you can not replace their elements. @@ -1388,52 +1415,67 @@ The type written as `~[mut T]` is a vector with mutable elements. Mutable vector literals are written `~[mut]` (empty) or `~[mut 1, 2, 3]` (with elements). +~~~~ +# enum crayon { almond, antique_brass, apricot, +# aquamarine, asparagus, atomic_tangerine, +# banana_mania, beaver, bittersweet }; + +let crayons = ~[mut banana_mania, beaver, bittersweet]; +crayons[0] = atomic_tangerine; +~~~~ + The `+` operator means concatenation when applied to vector types. -Growing a vector in Rust is not as inefficient as it looks : ~~~~ -let mut myvec = ~[], i = 0; -while i < 100 { - myvec += ~[i]; - i += 1; -} +# enum crayon { almond, antique_brass, apricot, +# aquamarine, asparagus, atomic_tangerine, +# banana_mania, beaver, bittersweet }; + +let my_crayons = ~[almond, antique_brass, apricot]; +let your_crayons = ~[banana_mania, beaver, bittersweet]; + +let our_crayons = my_crayons + your_crayons; ~~~~ -Because a vector is unique, replacing it with a longer one (which is -what `+= ~[i]` does) is indistinguishable from appending to it -in-place. Vector representations are optimized to grow -logarithmically, so the above code generates about the same amount of -copying and reallocation as `push` implementations in most other -languages. +The `+=` operator also works as expected, provided the assignee +lives in a mutable slot. + +~~~~ +# enum crayon { almond, antique_brass, apricot, +# aquamarine, asparagus, atomic_tangerine, +# banana_mania, beaver, bittersweet }; -> ***Note:*** Actually, currently, growing a vector is *exactly* as -> inefficient as it looks, since vector `+` has been moved to the -> libraries and Rust's operator overloading support is insufficient to -> allow this optimization. Try using `vec::push`. +let mut my_crayons = ~[almond, antique_brass, apricot]; +let your_crayons = ~[banana_mania, beaver, bittersweet]; + +my_crayons += your_crayons; +~~~~ ## Strings -The `str` type in Rust is represented exactly the same way as a vector -of bytes (`~[u8]`), except that it is guaranteed to have a trailing -null byte (for interoperability with C APIs). +The `str` type in Rust is represented exactly the same way as a unique +vector of immutable bytes (`~[u8]`). This sequence of bytes is +interpreted as an UTF-8 encoded sequence of characters. This has the +advantage that UTF-8 encoded I/O (which should really be the default +for modern systems) is very fast, and that strings have, for most +intents and purposes, a nicely compact representation. It has the +disadvantage that you only get constant-time access by byte, not by +character. -This sequence of bytes is interpreted as an UTF-8 encoded sequence of -characters. This has the advantage that UTF-8 encoded I/O (which -should really be the default for modern systems) is very fast, and -that strings have, for most intents and purposes, a nicely compact -representation. It has the disadvantage that you only get -constant-time access by byte, not by character. +~~~~ +let huh = "what?"; +let que: u8 = huh[4]; // indexing a string returns a `u8` +assert que == '?' as u8; +~~~~ A lot of algorithms don't need constant-time indexed access (they iterate over all characters, which `str::chars` helps with), and for those that do, many don't need actual characters, and can operate on bytes. For algorithms that do really need to index by character, -there's the option to convert your string to a character vector (using -`str::chars`). +there are core library functions available. -Like vectors, strings are always unique. You can wrap them in a shared -box to share them. Unlike vectors, there is no mutable variant of -strings. They are always immutable. +> ***Note:*** like vectors, strings will soon be allocatable in +> the local heap and on the stack, in addition to the exchange heap. # Closures From 4083e8518dad74b94ec6db49a5a1a2c65d8a476c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 17:54:13 -0700 Subject: [PATCH 27/32] tutorial: Add a section on common vector methods --- doc/tutorial.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/doc/tutorial.md b/doc/tutorial.md index b8eb9fdc6e878..437f07830c3d1 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1477,6 +1477,50 @@ there are core library functions available. > ***Note:*** like vectors, strings will soon be allocatable in > the local heap and on the stack, in addition to the exchange heap. +## Vector and string methods + +Both vectors and strings support a number of useful +[methods](#implementation). While we haven't covered methods yet, +most vector functionality is provided by methods, so let's have a +brief look at a few common ones. + +~~~ +# import io::println; +# enum crayon { +# almond, antique_brass, apricot, +# aquamarine, asparagus, atomic_tangerine, +# banana_mania, beaver, bittersweet +# } +# fn unwrap_crayon(c: crayon) -> int { 0 } +# fn eat_crayon_wax(i: int) { } +# fn store_crayon_in_nasal_cavity(i: uint, c: crayon) { } +# fn crayon_to_str(c: crayon) -> str { "" } + +let crayons = ~[almond, antique_brass, apricot]; + +// Check the length of the vector +assert crayons.len() == 3; +assert !crayons.is_empty(); + +// Iterate over a vector +for crayons.each |crayon| { + let delicious_crayon_wax = unwrap_crayon(crayon); + eat_crayon_wax(delicious_crayon_wax); +} + +// Map vector elements +let crayon_names = crayons.map(crayon_to_str); +let favorite_crayon_name = crayon_names[0]; + +// Remove whitespace from before and after the string +let new_favorite_crayon_name = favorite_crayon_name.trim(); + +if favorite_crayon_name.len() > 5 { + // Create a substring + println(favorite_crayon_name.substr(0, 5)); +} +~~~ + # Closures Named functions, like those in the previous section, may not refer From f5db5656ba76ff5e88e676b58a2f3659089c0739 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 18:00:16 -0700 Subject: [PATCH 28/32] tutorial: Add a few words about shadowing --- doc/tutorial.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/tutorial.md b/doc/tutorial.md index 437f07830c3d1..e0eba9b8681fe 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -380,6 +380,14 @@ fn main() { } ~~~~ +Local variables may shadow earlier declarations, causing the +previous variable to go out of scope. + +~~~~ +let my_favorite_value: float = 57.8; +let my_favorite_value: int = my_favorite_value as int; +~~~~ + ## Types The `-> bool` in the `is_four` example is the way a function's return From f96ec948867e22aa1ed36694e87ea878f2c97f5f Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 18:03:32 -0700 Subject: [PATCH 29/32] tutorial: Typo --- doc/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index e0eba9b8681fe..e129d41b6852e 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -1178,7 +1178,7 @@ Rust has three competing goals that inform its view of memory: * Performance - high-performance low-level code tends to employ a number of allocation strategies. low-performance high-level code often uses a single, GC-based, heap allocation strategy -* Concurrency - Rust maintain memory safety guarantees even +* Concurrency - Rust must maintain memory safety guarantees even for code running in parallel ## How performance considerations influence the memory model From c243c11058d80a707f79df08cbf32a5d407c1639 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 18:33:57 -0700 Subject: [PATCH 30/32] Add Benjamin Herr to AUTHORS.txt --- AUTHORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index f710d6bd71f3f..34b10a8cc76a9 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -13,6 +13,7 @@ Armin Ronacher Austin Seipp Ben Blum Ben Striegel +Benjamin Herr Benjamin Jackman Benjamin Kircher Brendan Eich From 8f9744d92de8efaa153cfed7270e03850298e5dd Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 7 Jul 2012 18:15:59 -0700 Subject: [PATCH 31/32] tutorial: Cleanup --- doc/tutorial.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index cc421d962af18..c15135b998f73 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -478,9 +478,7 @@ a type error. Read about [single-variant enums](#single_variant_enum) further on if you need to create a type name that's not just a synonym. -## Literals - -### Numeric literals +## Numeric literals Integers can be written in decimal (`144`), hexadecimal (`0x90`), and binary (`0b10010000`) base. @@ -539,7 +537,7 @@ and `f64` can be used to create literals of a specific type. The suffix `f` can be used to write `float` literals without a dot or exponent: `3f`. -### Other literals +## Other literals The nil literal is written just like the type: `()`. The keywords `true` and `false` produce the boolean literals. @@ -1224,8 +1222,8 @@ Because of this Rust also introduces a global "exchange heap". Objects allocated here have _ownership semantics_, meaning that there is only a single variable that refers to them. For this reason they are refered to as _unique boxes_. All tasks may allocate objects on this -heap, then _move_ those allocations to other tasks, avoiding expensive -copies. +heap, then transfer ownership of those allocations to other tasks, +avoiding expensive copies. ## What to be aware of @@ -1531,10 +1529,9 @@ if favorite_crayon_name.len() > 5 { # Closures -Named functions, like those in the previous section, may not refer -to local variables decalared outside the function - they do not -close over their environment. For example you couldn't write the -following: +Named functions, like those we've seen so far, may not refer to local +variables decalared outside the function - they do not "close over +their environment". For example you couldn't write the following: ~~~~ {.ignore} let foo = 10; @@ -1557,10 +1554,11 @@ let closure = |arg| println(#fmt("captured_var=%d, arg=%d", captured_var, arg)); call_closure_with_ten(closure); ~~~~ -The types of the arguments are generally omitted, as is the return -type, because the compiler can almost always infer them. In the rare -case where the compiler needs assistance though, the arguments and -return types may be annotated. +Closures begin with the argument list between bars and are followed by +a single expression. The types of the arguments are generally omitted, +as is the return type, because the compiler can almost always infer +them. In the rare case where the compiler needs assistance though, the +arguments and return types may be annotated. ~~~~ # type mygoodness = fn(str) -> str; type what_the = int; @@ -1584,7 +1582,7 @@ position and cannot be stored in structures nor returned from functions. Despite the limitations stack closures are used pervasively in Rust code. -## Boxed closures +## Shared closures When you need to store a closure in a data structure, a stack closure will not do, since the compiler will refuse to let you store it. For From 487878175b4f62d7678e11690c2d98fcdd817c48 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sat, 7 Jul 2012 00:52:31 -0400 Subject: [PATCH 32/32] Fix metadata serialization of foreign functions. Properly take the value of foreign functions from other crates to fix #1840. --- src/rustc/metadata/decoder.rs | 1 + src/rustc/metadata/encoder.rs | 2 +- src/rustc/middle/trans/base.rs | 12 +++++----- .../auxiliary/extern-crosscrate-source.rs | 22 +++++++++++++++++++ src/test/run-pass/extern-crosscrate.rs | 14 ++++++++++++ 5 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 src/test/auxiliary/extern-crosscrate-source.rs create mode 100644 src/test/run-pass/extern-crosscrate.rs diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs index 3380540786010..13829a188d9bc 100644 --- a/src/rustc/metadata/decoder.rs +++ b/src/rustc/metadata/decoder.rs @@ -288,6 +288,7 @@ fn item_to_def_like(item: ebml::doc, did: ast::def_id, cnum: ast::crate_num) 'u' { dl_def(ast::def_fn(did, ast::unsafe_fn)) } 'f' { dl_def(ast::def_fn(did, ast::impure_fn)) } 'p' { dl_def(ast::def_fn(did, ast::pure_fn)) } + 'F' { dl_def(ast::def_fn(did, ast::extern_fn)) } 'y' { dl_def(ast::def_ty(did)) } 't' { dl_def(ast::def_ty(did)) } 'm' { dl_def(ast::def_mod(did)) } diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 46a3cd84be3ba..1e50f1168c7b2 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -527,7 +527,7 @@ fn purity_fn_family(p: purity) -> char { unsafe_fn { 'u' } pure_fn { 'p' } impure_fn { 'f' } - extern_fn { 'c' } + extern_fn { 'F' } } } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 459b14038450e..5534140ae721f 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -2392,16 +2392,18 @@ fn lval_static_fn_inner(bcx: block, fn_id: ast::def_id, id: ast::node_id, ccx, node_id_type(bcx, id)))); } - // FIXME: Need to support extern-ABI functions (#1840) - if fn_id.crate == ast::local_crate { - alt bcx.tcx().def_map.find(id) { - some(ast::def_fn(_, ast::extern_fn)) { + alt ty::get(tpt.ty).struct { + ty::ty_fn(fn_ty) { + alt fn_ty.purity { + ast::extern_fn { // Extern functions are just opaque pointers let val = PointerCast(bcx, val, T_ptr(T_i8())); ret lval_no_env(bcx, val, owned_imm); } - _ { } + _ { /* fall through */ } } + } + _ { /* fall through */ } } ret {bcx: bcx, val: val, kind: owned, env: null_env}; diff --git a/src/test/auxiliary/extern-crosscrate-source.rs b/src/test/auxiliary/extern-crosscrate-source.rs new file mode 100644 index 0000000000000..55d08a0d0f47d --- /dev/null +++ b/src/test/auxiliary/extern-crosscrate-source.rs @@ -0,0 +1,22 @@ +#[link(name = "externcallback", + vers = "0.1")]; + +#[crate_type = "lib"]; + +extern mod rustrt { + fn rust_dbg_call(cb: *u8, + data: libc::uintptr_t) -> libc::uintptr_t; +} + +fn fact(n: uint) -> uint { + #debug("n = %?", n); + rustrt::rust_dbg_call(cb, n) +} + +extern fn cb(data: libc::uintptr_t) -> libc::uintptr_t { + if data == 1u { + data + } else { + fact(data - 1u) * data + } +} diff --git a/src/test/run-pass/extern-crosscrate.rs b/src/test/run-pass/extern-crosscrate.rs new file mode 100644 index 0000000000000..f3338475912ec --- /dev/null +++ b/src/test/run-pass/extern-crosscrate.rs @@ -0,0 +1,14 @@ +//aux-build:extern-crosscrate-source.rs + +use externcallback(vers = "0.1"); + +fn fact(n: uint) -> uint { + #debug("n = %?", n); + externcallback::rustrt::rust_dbg_call(externcallback::cb, n) +} + +fn main() { + let result = fact(10u); + #debug("result = %?", result); + assert result == 3628800u; +}