From f197d009796106fcba3c0bccf97bd704ec7d9604 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 6 Jul 2012 09:35:43 -0700 Subject: [PATCH 01/52] cargo: Fix building dependencies --- src/cargo/cargo.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cargo/cargo.rs b/src/cargo/cargo.rs index 5116d75f6f59c..efed7db81cf56 100644 --- a/src/cargo/cargo.rs +++ b/src/cargo/cargo.rs @@ -751,7 +751,7 @@ fn install_one_crate(c: cargo, path: str, cf: str) { #debug(" lib: %s", ct); install_to_dir(ct, c.libdir); } - } + } } @@ -799,6 +799,7 @@ fn install_source(c: cargo, path: str) { }; install_query(c, wd, query); + } os::change_dir(path); @@ -809,7 +810,6 @@ fn install_source(c: cargo, path: str) { } } } - } } fn install_git(c: cargo, wd: str, url: str, ref: option) { From 117b9a0b75564e0f437add6abbac81b9e7f432a1 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 7 Jun 2012 12:18:34 -0700 Subject: [PATCH 02/52] Basic functionality for new ports and chans First test using the new comm system. About twice the throughput of the old system. --- src/libcore/newcomm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/newcomm.rs b/src/libcore/newcomm.rs index e78b8551c6dd4..ec3c37d151d65 100644 --- a/src/libcore/newcomm.rs +++ b/src/libcore/newcomm.rs @@ -7,7 +7,7 @@ import arc::methods; import dvec::dvec; -import dvec::{extensions}; +import dvec::extensions; import sys::methods; export port; From 4dbd10a7024a30ca21571254166ebbe4a10b1beb Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 20 Jun 2012 11:53:13 -0700 Subject: [PATCH 03/52] First example of a program using pipes. --- src/libcore/vec.rs | 4 +- src/test/run-pass/pipe-manual-1.rs | 110 +++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/pipe-manual-1.rs diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 895377170bc0b..b3b9d089fea7a 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -1162,14 +1162,14 @@ pure fn unpack_mut_slice(s: &[mut T], impl extensions for ~[T] { #[inline(always)] - pure fn +(rhs: &[T]) -> ~[T] { + pure fn +(rhs: &[const T]) -> ~[T] { append(self, rhs) } } impl extensions for ~[mut T] { #[inline(always)] - pure fn +(rhs: &[mut T]) -> ~[mut T] { + pure fn +(rhs: &[const T]) -> ~[mut T] { append_mut(self, rhs) } } diff --git a/src/test/run-pass/pipe-manual-1.rs b/src/test/run-pass/pipe-manual-1.rs new file mode 100644 index 0000000000000..995e58ced31e6 --- /dev/null +++ b/src/test/run-pass/pipe-manual-1.rs @@ -0,0 +1,110 @@ +/* + +The first test case using pipes. The idea is to break this into +several stages for prototyping. Here's the plan: + +1. Write an already-compiled protocol using existing ports and chans. + +2. Take the already-compiled version and add the low-level +synchronization code instead. + +3. Write a syntax extension to compile the protocols. + +At some point, we'll need to add support for select. + +*/ + +mod pingpong { + import newcomm::*; + + type pingpong = ~mut option<(chan<()>, port<()>)>; + + fn init() -> (client::ping, server::ping) { + let cp = port(); + let sp = port(); + let cc = chan(sp); + let sc = chan(cp); + + let client = client::ping(~mut some((cc, cp))); + let server = server::ping(~mut some((sc, sp))); + + (client, server) + } + + mod client { + enum ping = pingpong; + enum pong = pingpong; + + fn do_ping(-c: ping) -> pong { + let mut op = none; + op <-> **c; + let (c, s) <- option::unwrap(op); + c.send(()); + let p <- (c, s); + pong(~mut some(p)) + } + + fn do_pong(-c: pong) -> (ping, ()) { + let mut op = none; + op <-> **c; + let (c, s) <- option::unwrap(op); + let d = s.recv(); + let p <- (c, s); + (ping(~mut some(p)), d) + } + } + + mod server { + enum ping = pingpong; + enum pong = pingpong; + + fn do_ping(-c: ping) -> (pong, ()) { + let mut op = none; + op <-> **c; + let (c, s) <- option::unwrap(op); + let d = s.recv(); + let p <- (c, s); + (pong(~mut some(p)), d) + } + + fn do_pong(-c: pong) -> ping { + let mut op = none; + op <-> **c; + let (c, s) <- option::unwrap(op); + c.send(()); + let p <- (c, s); + ping(~mut some(p)) + } + } +} + +fn client(-chan: pingpong::client::ping) { + let chan = pingpong::client::do_ping(chan); + log(error, "Sent ping"); + let (_chan, _data) = pingpong::client::do_pong(chan); + log(error, "Received pong"); +} + +fn server(-chan: pingpong::server::ping) { + let (chan, _data) = pingpong::server::do_ping(chan); + log(error, "Received ping"); + let _chan = pingpong::server::do_pong(chan); + log(error, "Sent pong"); +} + +fn main() { + let (client_, server_) = pingpong::init(); + let client_ = ~mut some(client_); + let server_ = ~mut some(server_); + + task::spawn {|move client_| + let mut client__ = none; + *client_ <-> client__; + client(option::unwrap(client__)); + }; + task::spawn {|move server_| + let mut server_ˊ = none; + *server_ <-> server_ˊ; + server(option::unwrap(server_ˊ)); + }; +} From 61be3cc19e4c01cb0d9f9fac3ed52a0819c8fcc5 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 22 Jun 2012 12:52:13 -0700 Subject: [PATCH 04/52] An example using pipes with most of the synchronization code in place. Fixed a bug in the atomic intrinsics where they wouldn't correctly return their old value. Pipes currently busy wait. The next step is to teach the scheduler how to deal with them. --- src/test/run-pass/pipe-manual-2.rs | 247 +++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 src/test/run-pass/pipe-manual-2.rs diff --git a/src/test/run-pass/pipe-manual-2.rs b/src/test/run-pass/pipe-manual-2.rs new file mode 100644 index 0000000000000..e0c77026f48f9 --- /dev/null +++ b/src/test/run-pass/pipe-manual-2.rs @@ -0,0 +1,247 @@ +/* +The first test case using pipes. The idea is to break this into +several stages for prototyping. Here's the plan: + +1. Write an already-compiled protocol using existing ports and chans. + +2. Take the already-compiled version and add the low-level +synchronization code instead. (That's what this file attempts to do) + +3. Write a syntax extension to compile the protocols. + +At some point, we'll need to add support for select. + +*/ + +// Hopefully someday we'll move this into core. +mod pipes { + import unsafe::{forget, reinterpret_cast}; + + enum state { + empty, + full, + blocked, + terminated + } + + type packet = { + mut state: state, + mut blocked_task: option, + mut payload: option + }; + + fn packet() -> *packet unsafe { + let p: *packet = unsafe::transmute(~{ + mut state: empty, + mut blocked_task: none::, + mut payload: none:: + }); + p + } + + #[abi = "rust-intrinsic"] + native mod rusti { + fn atomic_xchng(&dst: int, src: int) -> int; + fn atomic_xchng_acq(&dst: int, src: int) -> int; + fn atomic_xchng_rel(&dst: int, src: int) -> int; + } + + // We should consider moving this to core::unsafe, although I + // suspect graydon would want us to use void pointers instead. + unsafe fn uniquify(x: *T) -> ~T { + unsafe { unsafe::reinterpret_cast(x) } + } + + fn swap_state_acq(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_acq( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } + } + + fn swap_state_rel(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_rel( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } + } + + fn send(p: *packet, -payload: T) { + let p = unsafe { uniquify(p) }; + assert (*p).payload == none; + (*p).payload <- some(payload); + let old_state = swap_state_rel((*p).state, full); + alt old_state { + empty { + // Yay, fastpath. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + full { fail "duplicate send" } + blocked { + // FIXME: once the target will actually block, tell the + // scheduler to wake it up. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + terminated { + // The receiver will never receive this. Rely on drop_glue + // to clean everything up. + } + } + } + + fn recv(p: *packet) -> option { + let p = unsafe { uniquify(p) }; + loop { + let old_state = swap_state_acq((*p).state, + blocked); + alt old_state { + empty | blocked { task::yield(); } + full { + let mut payload = none; + payload <-> (*p).payload; + ret some(option::unwrap(payload)) + } + terminated { + assert old_state == terminated; + ret none; + } + } + } + } + + fn sender_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty | blocked { + // The receiver will eventually clean up. + unsafe { forget(p) } + } + full { + // This is impossible + fail "you dun goofed" + } + terminated { + // I have to clean up, use drop_glue + } + } + } + + fn receiver_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty { + // the sender will clean up + unsafe { forget(p) } + } + blocked { + // this shouldn't happen. + fail "terminating a blocked packet" + } + terminated | full { + // I have to clean up, use drop_glue + } + } + } +} + +mod pingpong { + enum ping = *pipes::packet; + enum pong = *pipes::packet; + + fn init() -> (client::ping, server::ping) { + let p = pipes::packet(); + let p = pingpong::ping(p); + + let client = client::ping(p); + let server = server::ping(p); + + (client, server) + } + + mod client { + enum ping = pingpong::ping; + enum pong = pingpong::pong; + + fn do_ping(-c: ping) -> pong { + let packet = pipes::packet(); + let packet = pingpong::pong(packet); + + pipes::send(**c, copy packet); + pong(packet) + } + + fn do_pong(-c: pong) -> (ping, ()) { + let packet = pipes::recv(**c); + alt packet { + none { + fail "sender closed the connection" + } + some(new_packet) { + (ping(new_packet), ()) + } + } + } + } + + mod server { + enum ping = pingpong::ping; + enum pong = pingpong::pong; + + fn do_ping(-c: ping) -> (pong, ()) { + let packet = pipes::recv(**c); + alt packet { + none { fail "sender closed the connection" } + some(new_packet) { + (pong(new_packet), ()) + } + } + } + + fn do_pong(-c: pong) -> ping { + let packet = pipes::packet(); + let packet = pingpong::ping(packet); + + pipes::send(**c, copy packet); + ping(packet) + } + } +} + +fn client(-chan: pingpong::client::ping) { + let chan = pingpong::client::do_ping(chan); + log(error, "Sent ping"); + let (chan, _data) = pingpong::client::do_pong(chan); + log(error, "Received pong"); + pipes::sender_terminate(**chan); +} + +fn server(-chan: pingpong::server::ping) { + let (chan, _data) = pingpong::server::do_ping(chan); + log(error, "Received ping"); + let chan = pingpong::server::do_pong(chan); + log(error, "Sent pong"); + pipes::receiver_terminate(**chan); +} + +fn main() { + let (client_, server_) = pingpong::init(); + let client_ = ~mut some(client_); + let server_ = ~mut some(server_); + + task::spawn {|move client_| + let mut client__ = none; + *client_ <-> client__; + client(option::unwrap(client__)); + }; + task::spawn {|move server_| + let mut server_ˊ = none; + *server_ <-> server_ˊ; + server(option::unwrap(server_ˊ)); + }; +} From c2d3cdc3dfe5cbf9a6a1d16e8bc57fe4532329da Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 25 Jun 2012 17:17:20 -0700 Subject: [PATCH 05/52] Progress towards pipes. --- src/test/run-pass/pipe-manual-2.rs | 98 +++++++++++++++++++----------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/src/test/run-pass/pipe-manual-2.rs b/src/test/run-pass/pipe-manual-2.rs index e0c77026f48f9..a547186bd664b 100644 --- a/src/test/run-pass/pipe-manual-2.rs +++ b/src/test/run-pass/pipe-manual-2.rs @@ -68,7 +68,8 @@ mod pipes { } } - fn send(p: *packet, -payload: T) { + fn send(-p: send_packet, -payload: T) { + let p = p.unwrap(); let p = unsafe { uniquify(p) }; assert (*p).payload == none; (*p).payload <- some(payload); @@ -95,7 +96,8 @@ mod pipes { } } - fn recv(p: *packet) -> option { + fn recv(-p: recv_packet) -> option { + let p = p.unwrap(); let p = unsafe { uniquify(p) }; loop { let old_state = swap_state_acq((*p).state, @@ -148,6 +150,45 @@ mod pipes { } } } + + class send_packet { + let mut p: option<*packet>; + new(p: *packet) { self.p = some(p); } + drop { + if self.p != none { + let mut p = none; + p <-> self.p; + sender_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } + } + + class recv_packet { + let mut p: option<*packet>; + new(p: *packet) { self.p = some(p); } + drop { + if self.p != none { + let mut p = none; + p <-> self.p; + receiver_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } + } + + fn entangle() -> (send_packet, recv_packet) { + let p = packet(); + (send_packet(p), recv_packet(p)) + } } mod pingpong { @@ -155,60 +196,45 @@ mod pingpong { enum pong = *pipes::packet; fn init() -> (client::ping, server::ping) { - let p = pipes::packet(); - let p = pingpong::ping(p); - - let client = client::ping(p); - let server = server::ping(p); - - (client, server) + pipes::entangle() } mod client { - enum ping = pingpong::ping; - enum pong = pingpong::pong; + type ping = pipes::send_packet; + type pong = pipes::recv_packet; fn do_ping(-c: ping) -> pong { - let packet = pipes::packet(); - let packet = pingpong::pong(packet); + let p = pipes::packet(); - pipes::send(**c, copy packet); - pong(packet) + pipes::send(c, pingpong::ping(p)); + pipes::recv_packet(p) } fn do_pong(-c: pong) -> (ping, ()) { - let packet = pipes::recv(**c); - alt packet { - none { + let packet = pipes::recv(c); + if packet == none { fail "sender closed the connection" - } - some(new_packet) { - (ping(new_packet), ()) - } } + (pipes::send_packet(*option::unwrap(packet)), ()) } } mod server { - enum ping = pingpong::ping; - enum pong = pingpong::pong; + type ping = pipes::recv_packet; + type pong = pipes::send_packet; fn do_ping(-c: ping) -> (pong, ()) { - let packet = pipes::recv(**c); - alt packet { - none { fail "sender closed the connection" } - some(new_packet) { - (pong(new_packet), ()) - } + let packet = pipes::recv(c); + if packet == none { + fail "sender closed the connection" } + (pipes::send_packet(*option::unwrap(packet)), ()) } fn do_pong(-c: pong) -> ping { - let packet = pipes::packet(); - let packet = pingpong::ping(packet); - - pipes::send(**c, copy packet); - ping(packet) + let p = pipes::packet(); + pipes::send(c, pingpong::pong(p)); + pipes::recv_packet(p) } } } @@ -218,7 +244,6 @@ fn client(-chan: pingpong::client::ping) { log(error, "Sent ping"); let (chan, _data) = pingpong::client::do_pong(chan); log(error, "Received pong"); - pipes::sender_terminate(**chan); } fn server(-chan: pingpong::server::ping) { @@ -226,7 +251,6 @@ fn server(-chan: pingpong::server::ping) { log(error, "Received ping"); let chan = pingpong::server::do_pong(chan); log(error, "Sent pong"); - pipes::receiver_terminate(**chan); } fn main() { From 0ad7ae523cca6cb6bb35037e37513d38423c2e05 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 26 Jun 2012 16:47:33 -0700 Subject: [PATCH 06/52] Macro and iface tricks to simulate self move. --- src/test/run-pass/pipe-manual-3.rs | 307 +++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 src/test/run-pass/pipe-manual-3.rs diff --git a/src/test/run-pass/pipe-manual-3.rs b/src/test/run-pass/pipe-manual-3.rs new file mode 100644 index 0000000000000..bdfde62ecc77e --- /dev/null +++ b/src/test/run-pass/pipe-manual-3.rs @@ -0,0 +1,307 @@ +/* +The first test case using pipes. The idea is to break this into +several stages for prototyping. Here's the plan: + +1. Write an already-compiled protocol using existing ports and chans. + +2. Take the already-compiled version and add the low-level +synchronization code instead. + +3. Write a syntax extension to compile the protocols. + +At some point, we'll need to add support for select. + +This file does horrible things to pretend we have self-move. + +*/ + +// Hopefully someday we'll move this into core. +mod pipes { + import unsafe::{forget, reinterpret_cast}; + + enum state { + empty, + full, + blocked, + terminated + } + + type packet = { + mut state: state, + mut blocked_task: option, + mut payload: option + }; + + fn packet() -> *packet unsafe { + let p: *packet = unsafe::transmute(~{ + mut state: empty, + mut blocked_task: none::, + mut payload: none:: + }); + p + } + + #[abi = "rust-intrinsic"] + native mod rusti { + fn atomic_xchng(&dst: int, src: int) -> int; + fn atomic_xchng_acq(&dst: int, src: int) -> int; + fn atomic_xchng_rel(&dst: int, src: int) -> int; + } + + // We should consider moving this to core::unsafe, although I + // suspect graydon would want us to use void pointers instead. + unsafe fn uniquify(x: *T) -> ~T { + unsafe { unsafe::reinterpret_cast(x) } + } + + fn swap_state_acq(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_acq( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } + } + + fn swap_state_rel(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_rel( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } + } + + fn send(-p: send_packet, -payload: T) { + let p = p.unwrap(); + let p = unsafe { uniquify(p) }; + assert (*p).payload == none; + (*p).payload <- some(payload); + let old_state = swap_state_rel((*p).state, full); + alt old_state { + empty { + // Yay, fastpath. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + full { fail "duplicate send" } + blocked { + // FIXME: once the target will actually block, tell the + // scheduler to wake it up. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + terminated { + // The receiver will never receive this. Rely on drop_glue + // to clean everything up. + } + } + } + + fn recv(-p: recv_packet) -> option { + let p = p.unwrap(); + let p = unsafe { uniquify(p) }; + loop { + let old_state = swap_state_acq((*p).state, + blocked); + alt old_state { + empty | blocked { task::yield(); } + full { + let mut payload = none; + payload <-> (*p).payload; + ret some(option::unwrap(payload)) + } + terminated { + assert old_state == terminated; + ret none; + } + } + } + } + + fn sender_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty | blocked { + // The receiver will eventually clean up. + unsafe { forget(p) } + } + full { + // This is impossible + fail "you dun goofed" + } + terminated { + // I have to clean up, use drop_glue + } + } + } + + fn receiver_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty { + // the sender will clean up + unsafe { forget(p) } + } + blocked { + // this shouldn't happen. + fail "terminating a blocked packet" + } + terminated | full { + // I have to clean up, use drop_glue + } + } + } + + class send_packet { + let mut p: option<*packet>; + new(p: *packet) { self.p = some(p); } + drop { + if self.p != none { + let mut p = none; + p <-> self.p; + sender_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } + } + + class recv_packet { + let mut p: option<*packet>; + new(p: *packet) { self.p = some(p); } + drop { + if self.p != none { + let mut p = none; + p <-> self.p; + receiver_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } + } + + fn entangle() -> (send_packet, recv_packet) { + let p = packet(); + (send_packet(p), recv_packet(p)) + } +} + +mod pingpong { + enum ping { ping, } + enum ping_message = *pipes::packet; + enum pong { pong, } + enum pong_message = *pipes::packet; + + fn init() -> (client::ping, server::ping) { + pipes::entangle() + } + + mod client { + type ping = pipes::send_packet; + type pong = pipes::recv_packet; + } + + impl abominable for client::ping { + fn send() -> fn@(-client::ping, ping) -> client::pong { + {|pipe, data| + let p = pipes::packet(); + pipes::send(pipe, pingpong::ping_message(p)); + pipes::recv_packet(p) + } + } + } + + impl abominable for client::pong { + fn recv() -> fn@(-client::pong) -> (client::ping, pong) { + {|pipe| + let packet = pipes::recv(pipe); + if packet == none { + fail "sender closed the connection" + } + let p : pong_message = option::unwrap(packet); + (pipes::send_packet(*p), pong) + } + } + } + + mod server { + type ping = pipes::recv_packet; + type pong = pipes::send_packet; + } + + impl abominable for server::ping { + fn recv() -> fn@(-server::ping) -> (server::pong, ping) { + {|pipe| + let packet = pipes::recv(pipe); + if packet == none { + fail "sender closed the connection" + } + let p : ping_message = option::unwrap(packet); + (pipes::send_packet(*p), ping) + } + } + } + + impl abominable for server::pong { + fn send() -> fn@(-server::pong, pong) -> server::ping { + {|pipe, data| + let p = pipes::packet(); + pipes::send(pipe, pingpong::pong_message(p)); + pipes::recv_packet(p) + } + } + } +} + +mod test { + import pingpong::{ping, pong, abominable}; + + fn macros() { + #macro[ + [#send[chan, data, ...], + chan.send()(chan, data, ...)] + ]; + #macro[ + [#recv[chan], + chan.recv()(chan)] + ]; + } + + fn client(-chan: pingpong::client::ping) { + let chan = #send(chan, ping); + log(error, "Sent ping"); + let (chan, _data) = #recv(chan); + log(error, "Received pong"); + } + + fn server(-chan: pingpong::server::ping) { + let (chan, _data) = #recv(chan); + log(error, "Received ping"); + let chan = #send(chan, pong); + log(error, "Sent pong"); + } +} + +fn main() { + let (client_, server_) = pingpong::init(); + let client_ = ~mut some(client_); + let server_ = ~mut some(server_); + + task::spawn {|move client_| + let mut client__ = none; + *client_ <-> client__; + test::client(option::unwrap(client__)); + }; + task::spawn {|move server_| + let mut server_ˊ = none; + *server_ <-> server_ˊ; + test::server(option::unwrap(server_ˊ)); + }; +} From 5d35435fac4f44d21bdeb529e525adce67838e04 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 26 Jun 2012 17:09:43 -0700 Subject: [PATCH 07/52] send only takes one data argument. --- src/test/run-pass/pipe-manual-3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/run-pass/pipe-manual-3.rs b/src/test/run-pass/pipe-manual-3.rs index bdfde62ecc77e..4e0500ce1347e 100644 --- a/src/test/run-pass/pipe-manual-3.rs +++ b/src/test/run-pass/pipe-manual-3.rs @@ -265,8 +265,8 @@ mod test { fn macros() { #macro[ - [#send[chan, data, ...], - chan.send()(chan, data, ...)] + [#send[chan, data], + chan.send()(chan, data)] ]; #macro[ [#recv[chan], From 5c3889a02f107a3c93e05e8834673f924113c161 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 29 Jun 2012 16:44:16 -0700 Subject: [PATCH 08/52] Contracts work well enough to do the message ring benchmark, and it's really fast. Fixing old-style vector, and xfail-prettying th contracts test because the pretty printer is unhappy. --- src/libcore/vec.rs | 1 + src/test/bench/msgsend-ring-contracts.rs | 326 +++++++++++++++++++++++ 2 files changed, 327 insertions(+) create mode 100644 src/test/bench/msgsend-ring-contracts.rs diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index b3b9d089fea7a..035be3d5d5d52 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -1163,6 +1163,7 @@ pure fn unpack_mut_slice(s: &[mut T], impl extensions for ~[T] { #[inline(always)] pure fn +(rhs: &[const T]) -> ~[T] { +he pretty printer is unhappy. append(self, rhs) } } diff --git a/src/test/bench/msgsend-ring-contracts.rs b/src/test/bench/msgsend-ring-contracts.rs new file mode 100644 index 0000000000000..2802fec90e1df --- /dev/null +++ b/src/test/bench/msgsend-ring-contracts.rs @@ -0,0 +1,326 @@ +// This test creates a bunch of tasks that simultaneously send to each +// other in a ring. The messages should all be basically +// independent. It's designed to hammer the global kernel lock, so +// that things will look really good once we get that lock out of the +// message path. + +// This version uses semi-automatically compiled channel contracts. + +// xfail-pretty + +import future::future; + +use std; +import std::time; + +import ring::server::recv; + +mod pipes { + // Runtime support for pipes. + + import unsafe::{forget, reinterpret_cast}; + + enum state { + empty, + full, + blocked, + terminated + } + + type packet = { + mut state: state, + mut blocked_task: option, + mut payload: option + }; + + fn packet() -> *packet unsafe { + let p: *packet = unsafe::transmute(~{ + mut state: empty, + mut blocked_task: none::, + mut payload: none:: + }); + p + } + + #[abi = "rust-intrinsic"] + native mod rusti { + fn atomic_xchng(&dst: int, src: int) -> int; + fn atomic_xchng_acq(&dst: int, src: int) -> int; + fn atomic_xchng_rel(&dst: int, src: int) -> int; + } + + // We should consider moving this to core::unsafe, although I + // suspect graydon would want us to use void pointers instead. + unsafe fn uniquify(x: *T) -> ~T { + unsafe { unsafe::reinterpret_cast(x) } + } + + fn swap_state_acq(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_acq( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } + } + + fn swap_state_rel(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_rel( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } + } + + fn send(-p: send_packet, -payload: T) { + let p = p.unwrap(); + let p = unsafe { uniquify(p) }; + assert (*p).payload == none; + (*p).payload <- some(payload); + let old_state = swap_state_rel((*p).state, full); + alt old_state { + empty { + // Yay, fastpath. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + full { fail "duplicate send" } + blocked { + // FIXME: once the target will actually block, tell the + // scheduler to wake it up. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + terminated { + // The receiver will never receive this. Rely on drop_glue + // to clean everything up. + } + } + } + + fn recv(-p: recv_packet) -> option { + let p = p.unwrap(); + let p = unsafe { uniquify(p) }; + loop { + let old_state = swap_state_acq((*p).state, + blocked); + alt old_state { + empty | blocked { task::yield(); } + full { + let mut payload = none; + payload <-> (*p).payload; + ret some(option::unwrap(payload)) + } + terminated { + assert old_state == terminated; + ret none; + } + } + } + } + + fn sender_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty | blocked { + // The receiver will eventually clean up. + unsafe { forget(p) } + } + full { + // This is impossible + fail "you dun goofed" + } + terminated { + // I have to clean up, use drop_glue + } + } + } + + fn receiver_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty { + // the sender will clean up + unsafe { forget(p) } + } + blocked { + // this shouldn't happen. + fail "terminating a blocked packet" + } + terminated | full { + // I have to clean up, use drop_glue + } + } + } + + class send_packet { + let mut p: option<*packet>; + new(p: *packet) { + //#error("take send %?", p); + self.p = some(p); + } + drop { + //if self.p != none { + // #error("drop send %?", option::get(self.p)); + //} + if self.p != none { + let mut p = none; + p <-> self.p; + sender_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } + } + + class recv_packet { + let mut p: option<*packet>; + new(p: *packet) { + //#error("take recv %?", p); + self.p = some(p); + } + drop { + //if self.p != none { + // #error("drop recv %?", option::get(self.p)); + //} + if self.p != none { + let mut p = none; + p <-> self.p; + receiver_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } + } + + fn entangle() -> (send_packet, recv_packet) { + let p = packet(); + (send_packet(p), recv_packet(p)) + } +} + +// This module was generated by the pipe compiler. +mod ring { + fn init() -> (client::num, server::num) { pipes::entangle() } + enum num { num(uint, server::num), } + mod client { + fn num(-pipe: num, x_0: uint) -> num { + let (c, s) = pipes::entangle(); + let message = ring::num(x_0, s); + pipes::send(pipe, message); + c + } + type num = pipes::send_packet; + } + mod server { + impl recv for num { + fn recv() -> extern fn(-num) -> ring::num { + fn recv(-pipe: num) -> ring::num { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type num = pipes::recv_packet; + } +} + +fn macros() { + #macro[ + [#recv[chan], + chan.recv()(chan)] + ]; + + #macro[ + [#move[x], + unsafe { let y <- *ptr::addr_of(x); y }] + ]; +} + +fn thread_ring(i: uint, + count: uint, + +num_chan: ring::client::num, + +num_port: ring::server::num) { + let mut num_chan <- some(num_chan); + let mut num_port <- some(num_port); + // Send/Receive lots of messages. + for uint::range(0u, count) {|j| + //#error("task %?, iter %?", i, j); + let mut num_chan2 = none; + let mut num_port2 = none; + num_chan2 <-> num_chan; + num_port2 <-> num_port; + num_chan = some(ring::client::num(option::unwrap(num_chan2), i * j)); + let port = option::unwrap(num_port2); + alt (#recv(port)) { + ring::num(_n, p) { + //log(error, _n); + num_port = some(#move(p)); + } + } + }; +} + +fn main(args: [str]/~) { + let args = if os::getenv("RUST_BENCH").is_some() { + ["", "100", "10000"]/~ + } else if args.len() <= 1u { + ["", "100", "1000"]/~ + } else { + copy args + }; + + let num_tasks = option::get(uint::from_str(args[1])); + let msg_per_task = option::get(uint::from_str(args[2])); + + let (num_chan, num_port) = ring::init(); + let mut num_chan = some(num_chan); + + let start = time::precise_time_s(); + + // create the ring + let mut futures = []/~; + + for uint::range(1u, num_tasks) {|i| + //#error("spawning %?", i); + let (new_chan, num_port) = ring::init(); + let num_chan2 = ~mut none; + *num_chan2 <-> num_chan; + let num_port = ~mut some(num_port); + futures += [future::spawn {|move num_chan2, move num_port| + let mut num_chan = none; + num_chan <-> *num_chan2; + let mut num_port1 = none; + num_port1 <-> *num_port; + thread_ring(i, msg_per_task, + option::unwrap(num_chan), + option::unwrap(num_port1)) + }]/~; + num_chan = some(new_chan); + }; + + // do our iteration + thread_ring(0u, msg_per_task, option::unwrap(num_chan), num_port); + + // synchronize + for futures.each {|f| f.get() }; + + let stop = time::precise_time_s(); + + // all done, report stats. + let num_msgs = num_tasks * msg_per_task; + let elapsed = (stop - start); + let rate = (num_msgs as float) / elapsed; + + io::println(#fmt("Sent %? messages in %? seconds", + num_msgs, elapsed)); + io::println(#fmt(" %? messages / second", rate)); + io::println(#fmt(" %? μs / message", 1000000. / rate)); +} From 67b0760592e1cf9aad2e84f1534ef08c3c5f1a2b Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 29 Jun 2012 18:15:28 -0700 Subject: [PATCH 09/52] Moved pipes runtime support to libcore, and add a test that will help verify that busy waiting is no longer happening. Fixing the result of a bad merge. --- src/libcore/core.rc | 6 +- src/libcore/pipes.rs | 207 +++++++++++++++++++++++ src/libcore/vec.rs | 1 - src/test/bench/msgsend-ring-contracts.rs | 191 --------------------- src/test/run-pass/pipe-sleep.rs | 59 +++++++ 5 files changed, 270 insertions(+), 194 deletions(-) create mode 100644 src/libcore/pipes.rs create mode 100644 src/test/run-pass/pipe-sleep.rs diff --git a/src/libcore/core.rc b/src/libcore/core.rc index d4e9180066096..97eefd708f328 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -39,7 +39,7 @@ export float, f32, f64; export box, char, str, ptr, vec, bool; export either, option, result, iter; export libc, os, io, run, rand, sys, unsafe, logging; -export arc, newcomm, comm, task, future; +export arc, newcomm, comm, task, future, pipes; export extfmt; export tuple; export to_str, to_bytes; @@ -187,7 +187,9 @@ mod newcomm; mod comm; mod task; mod future; - +// TODO: remove the conditionals once a new snapshot happens +#[cfg(stage1)] +mod pipes; // Runtime and language-primitive support diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs new file mode 100644 index 0000000000000..75a4b90af067a --- /dev/null +++ b/src/libcore/pipes.rs @@ -0,0 +1,207 @@ +// Runtime support for pipes. + +import unsafe::{forget, reinterpret_cast}; + +enum state { + empty, + full, + blocked, + terminated +} + +type packet = { + mut state: state, + mut blocked_task: option, + mut payload: option +}; + +fn packet() -> *packet unsafe { + let p: *packet = unsafe::transmute(~{ + mut state: empty, + mut blocked_task: none::, + mut payload: none:: + }); + p +} + +#[abi = "rust-intrinsic"] +native mod rusti { + fn atomic_xchng(&dst: int, src: int) -> int; + fn atomic_xchng_acq(&dst: int, src: int) -> int; + fn atomic_xchng_rel(&dst: int, src: int) -> int; +} + +// We should consider moving this to core::unsafe, although I +// suspect graydon would want us to use void pointers instead. +unsafe fn uniquify(x: *T) -> ~T { + unsafe { unsafe::reinterpret_cast(x) } +} + +fn swap_state_acq(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_acq( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } +} + +fn swap_state_rel(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_rel( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } +} + +fn send(-p: send_packet, -payload: T) { + let p = p.unwrap(); + let p = unsafe { uniquify(p) }; + assert (*p).payload == none; + (*p).payload <- some(payload); + let old_state = swap_state_rel((*p).state, full); + alt old_state { + empty { + // Yay, fastpath. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + full { fail "duplicate send" } + blocked { + // TODO: once the target will actually block, tell the + // scheduler to wake it up. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + terminated { + // The receiver will never receive this. Rely on drop_glue + // to clean everything up. + } + } +} + +fn recv(-p: recv_packet) -> option { + let p = p.unwrap(); + let p = unsafe { uniquify(p) }; + loop { + let old_state = swap_state_acq((*p).state, + blocked); + alt old_state { + empty | blocked { task::yield(); } + full { + let mut payload = none; + payload <-> (*p).payload; + ret some(option::unwrap(payload)) + } + terminated { + assert old_state == terminated; + ret none; + } + } + } +} + +fn sender_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty | blocked { + // The receiver will eventually clean up. + unsafe { forget(p) } + } + full { + // This is impossible + fail "you dun goofed" + } + terminated { + // I have to clean up, use drop_glue + } + } +} + +fn receiver_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty { + // the sender will clean up + unsafe { forget(p) } + } + blocked { + // this shouldn't happen. + fail "terminating a blocked packet" + } + terminated | full { + // I have to clean up, use drop_glue + } + } +} + +class send_packet { + let mut p: option<*packet>; + new(p: *packet) { + //#error("take send %?", p); + self.p = some(p); + } + drop { + //if self.p != none { + // #error("drop send %?", option::get(self.p)); + //} + if self.p != none { + let mut p = none; + p <-> self.p; + sender_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } +} + +class recv_packet { + let mut p: option<*packet>; + new(p: *packet) { + //#error("take recv %?", p); + self.p = some(p); + } + drop { + //if self.p != none { + // #error("drop recv %?", option::get(self.p)); + //} + if self.p != none { + let mut p = none; + p <-> self.p; + receiver_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } +} + +fn entangle() -> (send_packet, recv_packet) { + let p = packet(); + (send_packet(p), recv_packet(p)) +} + +fn spawn_service( + init: native fn() -> (send_packet, recv_packet), + +service: fn~(+recv_packet)) + -> send_packet +{ + let (client, server) = init(); + + // This is some nasty gymnastics required to safely move the pipe + // into a new task. + let server = ~mut some(server); + task::spawn() {|move service| + let mut server_ = none; + server_ <-> *server; + service(option::unwrap(server_)) + } + + client +} diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 035be3d5d5d52..b3b9d089fea7a 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -1163,7 +1163,6 @@ pure fn unpack_mut_slice(s: &[mut T], impl extensions for ~[T] { #[inline(always)] pure fn +(rhs: &[const T]) -> ~[T] { -he pretty printer is unhappy. append(self, rhs) } } diff --git a/src/test/bench/msgsend-ring-contracts.rs b/src/test/bench/msgsend-ring-contracts.rs index 2802fec90e1df..b3e69e5699df4 100644 --- a/src/test/bench/msgsend-ring-contracts.rs +++ b/src/test/bench/msgsend-ring-contracts.rs @@ -15,197 +15,6 @@ import std::time; import ring::server::recv; -mod pipes { - // Runtime support for pipes. - - import unsafe::{forget, reinterpret_cast}; - - enum state { - empty, - full, - blocked, - terminated - } - - type packet = { - mut state: state, - mut blocked_task: option, - mut payload: option - }; - - fn packet() -> *packet unsafe { - let p: *packet = unsafe::transmute(~{ - mut state: empty, - mut blocked_task: none::, - mut payload: none:: - }); - p - } - - #[abi = "rust-intrinsic"] - native mod rusti { - fn atomic_xchng(&dst: int, src: int) -> int; - fn atomic_xchng_acq(&dst: int, src: int) -> int; - fn atomic_xchng_rel(&dst: int, src: int) -> int; - } - - // We should consider moving this to core::unsafe, although I - // suspect graydon would want us to use void pointers instead. - unsafe fn uniquify(x: *T) -> ~T { - unsafe { unsafe::reinterpret_cast(x) } - } - - fn swap_state_acq(&dst: state, src: state) -> state { - unsafe { - reinterpret_cast(rusti::atomic_xchng_acq( - *(ptr::mut_addr_of(dst) as *mut int), - src as int)) - } - } - - fn swap_state_rel(&dst: state, src: state) -> state { - unsafe { - reinterpret_cast(rusti::atomic_xchng_rel( - *(ptr::mut_addr_of(dst) as *mut int), - src as int)) - } - } - - fn send(-p: send_packet, -payload: T) { - let p = p.unwrap(); - let p = unsafe { uniquify(p) }; - assert (*p).payload == none; - (*p).payload <- some(payload); - let old_state = swap_state_rel((*p).state, full); - alt old_state { - empty { - // Yay, fastpath. - - // The receiver will eventually clean this up. - unsafe { forget(p); } - } - full { fail "duplicate send" } - blocked { - // FIXME: once the target will actually block, tell the - // scheduler to wake it up. - - // The receiver will eventually clean this up. - unsafe { forget(p); } - } - terminated { - // The receiver will never receive this. Rely on drop_glue - // to clean everything up. - } - } - } - - fn recv(-p: recv_packet) -> option { - let p = p.unwrap(); - let p = unsafe { uniquify(p) }; - loop { - let old_state = swap_state_acq((*p).state, - blocked); - alt old_state { - empty | blocked { task::yield(); } - full { - let mut payload = none; - payload <-> (*p).payload; - ret some(option::unwrap(payload)) - } - terminated { - assert old_state == terminated; - ret none; - } - } - } - } - - fn sender_terminate(p: *packet) { - let p = unsafe { uniquify(p) }; - alt swap_state_rel((*p).state, terminated) { - empty | blocked { - // The receiver will eventually clean up. - unsafe { forget(p) } - } - full { - // This is impossible - fail "you dun goofed" - } - terminated { - // I have to clean up, use drop_glue - } - } - } - - fn receiver_terminate(p: *packet) { - let p = unsafe { uniquify(p) }; - alt swap_state_rel((*p).state, terminated) { - empty { - // the sender will clean up - unsafe { forget(p) } - } - blocked { - // this shouldn't happen. - fail "terminating a blocked packet" - } - terminated | full { - // I have to clean up, use drop_glue - } - } - } - - class send_packet { - let mut p: option<*packet>; - new(p: *packet) { - //#error("take send %?", p); - self.p = some(p); - } - drop { - //if self.p != none { - // #error("drop send %?", option::get(self.p)); - //} - if self.p != none { - let mut p = none; - p <-> self.p; - sender_terminate(option::unwrap(p)) - } - } - fn unwrap() -> *packet { - let mut p = none; - p <-> self.p; - option::unwrap(p) - } - } - - class recv_packet { - let mut p: option<*packet>; - new(p: *packet) { - //#error("take recv %?", p); - self.p = some(p); - } - drop { - //if self.p != none { - // #error("drop recv %?", option::get(self.p)); - //} - if self.p != none { - let mut p = none; - p <-> self.p; - receiver_terminate(option::unwrap(p)) - } - } - fn unwrap() -> *packet { - let mut p = none; - p <-> self.p; - option::unwrap(p) - } - } - - fn entangle() -> (send_packet, recv_packet) { - let p = packet(); - (send_packet(p), recv_packet(p)) - } -} - // This module was generated by the pipe compiler. mod ring { fn init() -> (client::num, server::num) { pipes::entangle() } diff --git a/src/test/run-pass/pipe-sleep.rs b/src/test/run-pass/pipe-sleep.rs new file mode 100644 index 0000000000000..0855acef51dcb --- /dev/null +++ b/src/test/run-pass/pipe-sleep.rs @@ -0,0 +1,59 @@ +use std; +import std::timer::sleep; +import std::uv; + +// Compiled by pipec +mod oneshot { + fn init() -> (client::waiting, server::waiting) { pipes::entangle() } + enum waiting { signal(server::signaled), } + enum signaled { } + mod client { + fn signal(-pipe: waiting) -> signaled { + let (c, s) = pipes::entangle(); + let message = oneshot::signal(s); + pipes::send(pipe, message); + c + } + type waiting = pipes::send_packet; + type signaled = pipes::send_packet; + } + mod server { + impl recv for waiting { + fn recv() -> extern fn(-waiting) -> oneshot::waiting { + fn recv(-pipe: waiting) -> oneshot::waiting { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type waiting = pipes::recv_packet; + impl recv for signaled { + fn recv() -> extern fn(-signaled) -> oneshot::signaled { + fn recv(-pipe: signaled) -> oneshot::signaled { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type signaled = pipes::recv_packet; + } +} + +fn main() { + import oneshot::client::*; + import oneshot::server::recv; + + #macro[ + [#recv[chan], + chan.recv()(chan)] + ]; + + let c = pipes::spawn_service(oneshot::init) {|p| + #recv(p); + }; + + let iotask = uv::global_loop::get(); + sleep(iotask, 5000); + + signal(c); +} \ No newline at end of file From a4838c93aadf4dbc32e71ddff19c6ecb6a95a66d Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 2 Jul 2012 11:38:45 -0700 Subject: [PATCH 10/52] Enabling pipes for all stages, and updating closure syntax. --- src/libcore/core.rc | 2 -- src/libcore/pipes.rs | 2 +- src/test/bench/msgsend-ring-contracts.rs | 10 +++++----- src/test/run-pass/pipe-manual-1.rs | 4 ++-- src/test/run-pass/pipe-manual-2.rs | 4 ++-- src/test/run-pass/pipe-manual-3.rs | 12 ++++++------ src/test/run-pass/pipe-sleep.rs | 4 +--- 7 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/libcore/core.rc b/src/libcore/core.rc index 97eefd708f328..de0e7fb8f6723 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -187,8 +187,6 @@ mod newcomm; mod comm; mod task; mod future; -// TODO: remove the conditionals once a new snapshot happens -#[cfg(stage1)] mod pipes; // Runtime and language-primitive support diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs index 75a4b90af067a..ae141cedc8292 100644 --- a/src/libcore/pipes.rs +++ b/src/libcore/pipes.rs @@ -197,7 +197,7 @@ fn spawn_service( // This is some nasty gymnastics required to safely move the pipe // into a new task. let server = ~mut some(server); - task::spawn() {|move service| + do task::spawn |move service| { let mut server_ = none; server_ <-> *server; service(option::unwrap(server_)) diff --git a/src/test/bench/msgsend-ring-contracts.rs b/src/test/bench/msgsend-ring-contracts.rs index b3e69e5699df4..99265353fe5de 100644 --- a/src/test/bench/msgsend-ring-contracts.rs +++ b/src/test/bench/msgsend-ring-contracts.rs @@ -60,7 +60,7 @@ fn thread_ring(i: uint, let mut num_chan <- some(num_chan); let mut num_port <- some(num_port); // Send/Receive lots of messages. - for uint::range(0u, count) {|j| + for uint::range(0u, count) |j| { //#error("task %?, iter %?", i, j); let mut num_chan2 = none; let mut num_port2 = none; @@ -97,13 +97,13 @@ fn main(args: [str]/~) { // create the ring let mut futures = []/~; - for uint::range(1u, num_tasks) {|i| + for uint::range(1u, num_tasks) |i| { //#error("spawning %?", i); let (new_chan, num_port) = ring::init(); let num_chan2 = ~mut none; *num_chan2 <-> num_chan; let num_port = ~mut some(num_port); - futures += [future::spawn {|move num_chan2, move num_port| + futures += [future::spawn(|move num_chan2, move num_port| { let mut num_chan = none; num_chan <-> *num_chan2; let mut num_port1 = none; @@ -111,7 +111,7 @@ fn main(args: [str]/~) { thread_ring(i, msg_per_task, option::unwrap(num_chan), option::unwrap(num_port1)) - }]/~; + })]/~; num_chan = some(new_chan); }; @@ -119,7 +119,7 @@ fn main(args: [str]/~) { thread_ring(0u, msg_per_task, option::unwrap(num_chan), num_port); // synchronize - for futures.each {|f| f.get() }; + for futures.each |f| { f.get() }; let stop = time::precise_time_s(); diff --git a/src/test/run-pass/pipe-manual-1.rs b/src/test/run-pass/pipe-manual-1.rs index 995e58ced31e6..7efedfe6418ca 100644 --- a/src/test/run-pass/pipe-manual-1.rs +++ b/src/test/run-pass/pipe-manual-1.rs @@ -97,12 +97,12 @@ fn main() { let client_ = ~mut some(client_); let server_ = ~mut some(server_); - task::spawn {|move client_| + do task::spawn |move client_| { let mut client__ = none; *client_ <-> client__; client(option::unwrap(client__)); }; - task::spawn {|move server_| + do task::spawn |move server_| { let mut server_ˊ = none; *server_ <-> server_ˊ; server(option::unwrap(server_ˊ)); diff --git a/src/test/run-pass/pipe-manual-2.rs b/src/test/run-pass/pipe-manual-2.rs index a547186bd664b..0619a7b6b44a1 100644 --- a/src/test/run-pass/pipe-manual-2.rs +++ b/src/test/run-pass/pipe-manual-2.rs @@ -258,12 +258,12 @@ fn main() { let client_ = ~mut some(client_); let server_ = ~mut some(server_); - task::spawn {|move client_| + do task::spawn |move client_| { let mut client__ = none; *client_ <-> client__; client(option::unwrap(client__)); }; - task::spawn {|move server_| + do task::spawn |move server_| { let mut server_ˊ = none; *server_ <-> server_ˊ; server(option::unwrap(server_ˊ)); diff --git a/src/test/run-pass/pipe-manual-3.rs b/src/test/run-pass/pipe-manual-3.rs index 4e0500ce1347e..905063ea713cb 100644 --- a/src/test/run-pass/pipe-manual-3.rs +++ b/src/test/run-pass/pipe-manual-3.rs @@ -210,7 +210,7 @@ mod pingpong { impl abominable for client::ping { fn send() -> fn@(-client::ping, ping) -> client::pong { - {|pipe, data| + |pipe, data| { let p = pipes::packet(); pipes::send(pipe, pingpong::ping_message(p)); pipes::recv_packet(p) @@ -220,7 +220,7 @@ mod pingpong { impl abominable for client::pong { fn recv() -> fn@(-client::pong) -> (client::ping, pong) { - {|pipe| + |pipe| { let packet = pipes::recv(pipe); if packet == none { fail "sender closed the connection" @@ -238,7 +238,7 @@ mod pingpong { impl abominable for server::ping { fn recv() -> fn@(-server::ping) -> (server::pong, ping) { - {|pipe| + |pipe| { let packet = pipes::recv(pipe); if packet == none { fail "sender closed the connection" @@ -251,7 +251,7 @@ mod pingpong { impl abominable for server::pong { fn send() -> fn@(-server::pong, pong) -> server::ping { - {|pipe, data| + |pipe, data| { let p = pipes::packet(); pipes::send(pipe, pingpong::pong_message(p)); pipes::recv_packet(p) @@ -294,12 +294,12 @@ fn main() { let client_ = ~mut some(client_); let server_ = ~mut some(server_); - task::spawn {|move client_| + do task::spawn |move client_| { let mut client__ = none; *client_ <-> client__; test::client(option::unwrap(client__)); }; - task::spawn {|move server_| + do task::spawn |move server_| { let mut server_ˊ = none; *server_ <-> server_ˊ; test::server(option::unwrap(server_ˊ)); diff --git a/src/test/run-pass/pipe-sleep.rs b/src/test/run-pass/pipe-sleep.rs index 0855acef51dcb..932a4f9a43221 100644 --- a/src/test/run-pass/pipe-sleep.rs +++ b/src/test/run-pass/pipe-sleep.rs @@ -48,9 +48,7 @@ fn main() { chan.recv()(chan)] ]; - let c = pipes::spawn_service(oneshot::init) {|p| - #recv(p); - }; + let c = pipes::spawn_service(oneshot::init, |p| { #recv(p); }); let iotask = uv::global_loop::get(); sleep(iotask, 5000); From e5c9cb2b3df91df4207d63611a1918e461219456 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 2 Jul 2012 17:42:58 -0700 Subject: [PATCH 11/52] Pipes sleep and wake properly. --- src/libcore/pipes.rs | 50 +++++++++++++++++++++++++++++++------- src/libcore/task.rs | 1 + src/rt/rust_builtin.cpp | 20 ++++++++++++++++ src/rt/rust_task.cpp | 53 ++++++++++++++++++++++++++++++++++++++--- src/rt/rust_task.h | 16 +++++++++++++ src/rt/rustrt.def.in | 3 +++ 6 files changed, 132 insertions(+), 11 deletions(-) diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs index ae141cedc8292..d6c5b32de59b5 100644 --- a/src/libcore/pipes.rs +++ b/src/libcore/pipes.rs @@ -11,7 +11,7 @@ enum state { type packet = { mut state: state, - mut blocked_task: option, + mut blocked_task: option<*rust_task>, mut payload: option }; @@ -31,6 +31,19 @@ native mod rusti { fn atomic_xchng_rel(&dst: int, src: int) -> int; } +type rust_task = libc::c_void; + +native mod rustrt { + #[rust_stack] + fn rust_get_task() -> *rust_task; + + #[rust_stack] + fn task_clear_event_reject(task: *rust_task); + + fn task_wait_event(this: *rust_task) -> *libc::c_void; + fn task_signal_event(target: *rust_task, event: *libc::c_void); +} + // We should consider moving this to core::unsafe, although I // suspect graydon would want us to use void pointers instead. unsafe fn uniquify(x: *T) -> ~T { @@ -54,8 +67,8 @@ fn swap_state_rel(&dst: state, src: state) -> state { } fn send(-p: send_packet, -payload: T) { - let p = p.unwrap(); - let p = unsafe { uniquify(p) }; + let p_ = p.unwrap(); + let p = unsafe { uniquify(p_) }; assert (*p).payload == none; (*p).payload <- some(payload); let old_state = swap_state_rel((*p).state, full); @@ -68,8 +81,13 @@ fn send(-p: send_packet, -payload: T) { } full { fail "duplicate send" } blocked { - // TODO: once the target will actually block, tell the - // scheduler to wake it up. + #debug("waking up task for %?", p_); + alt p.blocked_task { + some(task) { + rustrt::task_signal_event(task, p_ as *libc::c_void); + } + none { fail "blocked packet has no task" } + } // The receiver will eventually clean this up. unsafe { forget(p); } @@ -82,16 +100,32 @@ fn send(-p: send_packet, -payload: T) { } fn recv(-p: recv_packet) -> option { - let p = p.unwrap(); - let p = unsafe { uniquify(p) }; + let p_ = p.unwrap(); + let p = unsafe { uniquify(p_) }; + let this = rustrt::rust_get_task(); + rustrt::task_clear_event_reject(this); + p.blocked_task = some(this); loop { let old_state = swap_state_acq((*p).state, blocked); + #debug("%?", old_state); alt old_state { - empty | blocked { task::yield(); } + empty { + #debug("no data available on %?, going to sleep.", p_); + rustrt::task_wait_event(this); + #debug("woke up, p.state = %?", p.state); + if p.state == full { + let mut payload = none; + payload <-> (*p).payload; + p.state = terminated; + ret some(option::unwrap(payload)) + } + } + blocked { fail "blocking on already blocked packet" } full { let mut payload = none; payload <-> (*p).payload; + p.state = terminated; ret some(option::unwrap(payload)) } terminated { diff --git a/src/libcore/task.rs b/src/libcore/task.rs index fd69525d63e2d..7730ef8adfe7a 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -782,6 +782,7 @@ extern mod rustrt { fn rust_new_sched(num_threads: libc::uintptr_t) -> sched_id; fn get_task_id() -> task_id; + #[rust_stack] fn rust_get_task() -> *rust_task; fn new_task() -> *rust_task; diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index a33d3cb90fedb..732dbaa329311 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -922,6 +922,26 @@ rust_task_local_data_atexit(rust_task *task, void (*cleanup_fn)(void *data)) { task->task_local_data_cleanup = cleanup_fn; } +extern "C" void +task_clear_event_reject(rust_task *task) { + task->clear_event_reject(); +} + +// Waits on an event, returning the pointer to the event that unblocked this +// task. +extern "C" void * +task_wait_event(rust_task *task) { + // TODO: we should assert that the passed in task is the currently running + // task. We wouldn't want to wait some other task. + + return task->wait_event(); +} + +extern "C" void +task_signal_event(rust_task *target, void *event) { + target->signal_event(event); +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index d7d43bcd27d2b..6a9f5cf50012f 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -36,6 +36,8 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state, state(state), cond(NULL), cond_name("none"), + event_reject(false), + event(NULL), killed(false), reentered_rust_stack(false), disallow_kill(0), @@ -407,13 +409,20 @@ rust_task::free(void *p) void rust_task::transition(rust_task_state src, rust_task_state dst, rust_cond *cond, const char* cond_name) { + scoped_lock with(state_lock); + transition_locked(src, dst, cond, cond_name); +} + +void rust_task::transition_locked(rust_task_state src, rust_task_state dst, + rust_cond *cond, const char* cond_name) { + state_lock.must_have_lock(); sched_loop->transition(this, src, dst, cond, cond_name); } void rust_task::set_state(rust_task_state state, rust_cond *cond, const char* cond_name) { - scoped_lock with(state_lock); + state_lock.must_have_lock(); this->state = state; this->cond = cond; this->cond_name = cond_name; @@ -422,7 +431,11 @@ rust_task::set_state(rust_task_state state, bool rust_task::block(rust_cond *on, const char* name) { scoped_lock with(kill_lock); + return block_locked(on, name); +} +bool +rust_task::block_locked(rust_cond *on, const char* name) { if (must_fail_from_being_killed_unlocked()) { // We're already going to die. Don't block. Tell the task to fail return false; @@ -433,19 +446,25 @@ rust_task::block(rust_cond *on, const char* name) { assert(cond == NULL && "Cannot block an already blocked task."); assert(on != NULL && "Cannot block on a NULL object."); - transition(task_state_running, task_state_blocked, on, name); + transition_locked(task_state_running, task_state_blocked, on, name); return true; } void rust_task::wakeup(rust_cond *from) { + scoped_lock with(state_lock); + wakeup_locked(from); +} + +void +rust_task::wakeup_locked(rust_cond *from) { assert(cond != NULL && "Cannot wake up unblocked task."); LOG(this, task, "Blocked on 0x%" PRIxPTR " woken up on 0x%" PRIxPTR, (uintptr_t) cond, (uintptr_t) from); assert(cond == from && "Cannot wake up blocked task on wrong condition."); - transition(task_state_blocked, task_state_running, NULL, "none"); + transition_locked(task_state_blocked, task_state_running, NULL, "none"); } void @@ -693,6 +712,34 @@ rust_task::allow_kill() { disallow_kill--; } +void * +rust_task::wait_event() { + scoped_lock with(state_lock); + + if(!event_reject) { + block_locked(&event_cond, "waiting on event"); + bool killed = false; + state_lock.unlock(); + yield(&killed); + state_lock.lock(); + // TODO: what is the right thing to do if we are killed? + } + + event_reject = false; + return event; +} + +void +rust_task::signal_event(void *event) { + scoped_lock with(state_lock); + + this->event = event; + event_reject = true; + if(task_state_blocked == state) { + wakeup_locked(&event_cond); + } +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 96b0bddd4e06f..c5bdc50e43084 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -175,6 +175,10 @@ rust_task : public kernel_owned, rust_cond rust_cond *cond; const char *cond_name; + bool event_reject; + rust_cond event_cond; + void *event; + // Protects the killed flag, disallow_kill flag, reentered_rust_stack lock_and_signal kill_lock; // Indicates that the task was killed and needs to unwind @@ -205,6 +209,8 @@ rust_task : public kernel_owned, rust_cond void transition(rust_task_state src, rust_task_state dst, rust_cond *cond, const char* cond_name); + void transition_locked(rust_task_state src, rust_task_state dst, + rust_cond *cond, const char* cond_name); bool must_fail_from_being_killed_unlocked(); // Called by rust_task_fail to unwind on failure @@ -221,6 +227,9 @@ rust_task : public kernel_owned, rust_cond char const *file, size_t line); + bool block_locked(rust_cond *on, const char* name); + void wakeup_locked(rust_cond *from); + public: // Only a pointer to 'name' is kept, so it must live as long as this task. @@ -303,6 +312,13 @@ rust_task : public kernel_owned, rust_cond rust_cond *get_cond() { return cond; } const char *get_cond_name() { return cond_name; } + void clear_event_reject() { + this->event_reject = false; + } + + void *wait_event(); + void signal_event(void *event); + void cleanup_after_turn(); void inhibit_kill(); diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index a218782dcbe63..e674d6fa19774 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -64,6 +64,9 @@ start_task vec_reserve_shared str_reserve_shared vec_from_buf_shared +task_clear_event_reject +task_wait_event +task_signal_event unsupervise upcall_cmp_type upcall_fail From 89bdd481e59c5416f75668bc1b4782b57a167333 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 2 Jul 2012 19:03:11 -0700 Subject: [PATCH 12/52] Port future to pipes. Graph500 is about 21% faster now. Making all tests pass. --- src/libcore/future.rs | 97 ++++++++++++++++++++++++++++++++++--------- src/libcore/pipes.rs | 19 +++++++++ src/libcore/task.rs | 20 ++++++--- 3 files changed, 112 insertions(+), 24 deletions(-) diff --git a/src/libcore/future.rs b/src/libcore/future.rs index 322b75da7dae7..8bc46de26b297 100644 --- a/src/libcore/future.rs +++ b/src/libcore/future.rs @@ -22,7 +22,10 @@ export get; export with; export spawn; -/// The future type +// for task.rs +export future_pipe; + +#[doc = "The future type"] enum future = { mut v: either<@A, fn@() -> A> }; @@ -56,16 +59,34 @@ fn from_value(+val: A) -> future { }) } -fn from_port(-port: comm::port) -> future { - /*! - * Create a future from a port - * - * The first time that the value is requested the task will block - * waiting for the result to be received on the port. - */ +fn macros() { + #macro[ + [#recv[chan], + chan.recv()(chan)] + ]; + #macro[ + [#move[x], + unsafe { let y <- *ptr::addr_of(x); y }] + ]; +} - do from_fn { - comm::recv(port) +fn from_port(-port: future_pipe::client::waiting) -> future { + #[doc = " + Create a future from a port + + The first time that the value is requested the task will block + waiting for the result to be received on the port. + "]; + import future_pipe::client::recv; + + let port = ~mut some(port); + do from_fn |move port| { + let mut port_ = none; + port_ <-> *port; + let port = option::unwrap(port_); + alt (#recv(port)) { + future_pipe::completed(data, _next) { #move(data) } + } } } @@ -91,12 +112,9 @@ fn spawn(+blk: fn~() -> A) -> future { * value of the future. */ - let mut po = comm::port(); - let ch = comm::chan(po); - do task::spawn { - comm::send(ch, blk()) - }; - from_port(po) + from_port(pipes::spawn_service_recv(future_pipe::init, |ch| { + future_pipe::server::completed(ch, blk()); + })) } fn get(future: future) -> A { @@ -119,6 +137,48 @@ fn with(future: future, blk: fn(A) -> B) -> B { blk(*v) } +// The pipe protocol, generated by pipec +mod future_pipe { + fn init() -> (client::waiting, server::waiting) { + { let (s, c) = pipes::entangle(); (c, s) } + } + enum waiting { completed(T, client::terminated), } + enum terminated { } + mod client { + impl recv for waiting { + fn recv() -> extern fn(+waiting) -> future_pipe::waiting { + fn recv(+pipe: waiting) -> + future_pipe::waiting { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type waiting = pipes::recv_packet>; + type terminated = pipes::send_packet; + } + mod server { + fn completed(+pipe: waiting, +x_0: T) -> terminated { + { + let (s, c) = pipes::entangle(); + let message = future_pipe::completed(x_0, s); + pipes::send(pipe, message); + c + } + } + type waiting = pipes::send_packet>; + impl recv for terminated { + fn recv() -> extern fn(+terminated) -> future_pipe::terminated { + fn recv(+pipe: terminated) -> future_pipe::terminated { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type terminated = pipes::recv_packet; + } +} + #[test] fn test_from_value() { let f = from_value("snail"); @@ -127,9 +187,8 @@ fn test_from_value() { #[test] fn test_from_port() { - let po = comm::port(); - let ch = comm::chan(po); - comm::send(ch, "whale"); + let (po, ch) = future_pipe::init(); + future_pipe::server::completed(ch, "whale"); let f = from_port(po); assert get(f) == "whale"; } diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs index d6c5b32de59b5..88f695554397a 100644 --- a/src/libcore/pipes.rs +++ b/src/libcore/pipes.rs @@ -239,3 +239,22 @@ fn spawn_service( client } + +fn spawn_service_recv( + init: native fn() -> (recv_packet, send_packet), + +service: fn~(+send_packet)) + -> recv_packet +{ + let (client, server) = init(); + + // This is some nasty gymnastics required to safely move the pipe + // into a new task. + let server = ~mut some(server); + do task::spawn |move service| { + let mut server_ = none; + server_ <-> *server; + service(option::unwrap(server_)) + } + + client +} diff --git a/src/libcore/task.rs b/src/libcore/task.rs index 7730ef8adfe7a..2d2c2660fc880 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -308,11 +308,21 @@ fn future_result(builder: builder) -> future::future { fn future_task(builder: builder) -> future::future { //! Get a future representing the handle to the new task - let mut po = comm::port(); - let ch = comm::chan(po); - do add_wrapper(builder) |body| { - fn~() { - comm::send(ch, get_task()); + import future::future_pipe; + + let (po, ch) = future_pipe::init(); + + let ch = ~mut some(ch); + + do add_wrapper(builder) |body, move ch| { + let ch = { let mut t = none; + t <-> *ch; + ~mut t}; + fn~(move ch) { + let mut po = none; + po <-> *ch; + future_pipe::server::completed(option::unwrap(po), + get_task()); body(); } } From a787f4001388a394d5219b74113a718d980e4c90 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Tue, 3 Jul 2012 17:33:20 -0700 Subject: [PATCH 13/52] Select on pipes. Updating syntax and test cases. --- src/libcore/pipes.rs | 142 +++++++++++++++--- src/libcore/vec.rs | 43 +++++- src/test/bench/msgsend-ring-contracts.rs | 2 +- src/test/run-pass/pipe-manual-2.rs | 178 ----------------------- src/test/run-pass/pipe-manual-3.rs | 178 ----------------------- src/test/run-pass/pipe-select.rs | 125 ++++++++++++++++ 6 files changed, 284 insertions(+), 384 deletions(-) create mode 100644 src/test/run-pass/pipe-select.rs diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs index 88f695554397a..f1617c59ac7d3 100644 --- a/src/libcore/pipes.rs +++ b/src/libcore/pipes.rs @@ -9,23 +9,29 @@ enum state { terminated } -type packet = { +type packet_header = { mut state: state, mut blocked_task: option<*rust_task>, +}; + +type packet = { + header: packet_header, mut payload: option }; fn packet() -> *packet unsafe { let p: *packet = unsafe::transmute(~{ - mut state: empty, - mut blocked_task: none::, + header: { + mut state: empty, + mut blocked_task: none::, + }, mut payload: none:: }); p } #[abi = "rust-intrinsic"] -native mod rusti { +extern mod rusti { fn atomic_xchng(&dst: int, src: int) -> int; fn atomic_xchng_acq(&dst: int, src: int) -> int; fn atomic_xchng_rel(&dst: int, src: int) -> int; @@ -33,7 +39,7 @@ native mod rusti { type rust_task = libc::c_void; -native mod rustrt { +extern mod rustrt { #[rust_stack] fn rust_get_task() -> *rust_task; @@ -71,7 +77,7 @@ fn send(-p: send_packet, -payload: T) { let p = unsafe { uniquify(p_) }; assert (*p).payload == none; (*p).payload <- some(payload); - let old_state = swap_state_rel((*p).state, full); + let old_state = swap_state_rel(p.header.state, full); alt old_state { empty { // Yay, fastpath. @@ -82,9 +88,10 @@ fn send(-p: send_packet, -payload: T) { full { fail "duplicate send" } blocked { #debug("waking up task for %?", p_); - alt p.blocked_task { + alt p.header.blocked_task { some(task) { - rustrt::task_signal_event(task, p_ as *libc::c_void); + rustrt::task_signal_event( + task, ptr::addr_of(p.header) as *libc::c_void); } none { fail "blocked packet has no task" } } @@ -104,20 +111,20 @@ fn recv(-p: recv_packet) -> option { let p = unsafe { uniquify(p_) }; let this = rustrt::rust_get_task(); rustrt::task_clear_event_reject(this); - p.blocked_task = some(this); + p.header.blocked_task = some(this); loop { - let old_state = swap_state_acq((*p).state, + let old_state = swap_state_acq(p.header.state, blocked); #debug("%?", old_state); alt old_state { empty { #debug("no data available on %?, going to sleep.", p_); rustrt::task_wait_event(this); - #debug("woke up, p.state = %?", p.state); - if p.state == full { + #debug("woke up, p.state = %?", p.header.state); + if p.header.state == full { let mut payload = none; payload <-> (*p).payload; - p.state = terminated; + p.header.state = terminated; ret some(option::unwrap(payload)) } } @@ -125,7 +132,7 @@ fn recv(-p: recv_packet) -> option { full { let mut payload = none; payload <-> (*p).payload; - p.state = terminated; + p.header.state = terminated; ret some(option::unwrap(payload)) } terminated { @@ -138,7 +145,7 @@ fn recv(-p: recv_packet) -> option { fn sender_terminate(p: *packet) { let p = unsafe { uniquify(p) }; - alt swap_state_rel((*p).state, terminated) { + alt swap_state_rel(p.header.state, terminated) { empty | blocked { // The receiver will eventually clean up. unsafe { forget(p) } @@ -155,7 +162,7 @@ fn sender_terminate(p: *packet) { fn receiver_terminate(p: *packet) { let p = unsafe { uniquify(p) }; - alt swap_state_rel((*p).state, terminated) { + alt swap_state_rel(p.header.state, terminated) { empty { // the sender will clean up unsafe { forget(p) } @@ -170,15 +177,106 @@ fn receiver_terminate(p: *packet) { } } +impl private_methods for packet_header { + // Returns the old state. + fn mark_blocked(this: *rust_task) -> state { + self.blocked_task = some(this); + swap_state_acq(self.state, blocked) + } + + fn unblock() { + alt swap_state_acq(self.state, empty) { + empty | blocked { } + terminated { self.state = terminated; } + full { self.state = full; } + } + } +} + +#[doc = "Returns when one of the packet headers reports data is +available."] +fn wait_many(pkts: ~[&a.packet_header]) -> uint { + let this = rustrt::rust_get_task(); + + rustrt::task_clear_event_reject(this); + let mut data_avail = false; + let mut ready_packet = pkts.len(); + for pkts.eachi |i, p| { + let old = p.mark_blocked(this); + alt old { + full | terminated { + data_avail = true; + ready_packet = i; + p.state = old; + break; + } + blocked { fail "blocking on blocked packet" } + empty { } + } + } + + while !data_avail { + #debug("sleeping on %? packets", pkts.len()); + let event = rustrt::task_wait_event(this) as *packet_header; + let pos = vec::position(pkts, |p| ptr::addr_of(*p) == event); + + alt pos { + some(i) { + ready_packet = i; + data_avail = true; + } + none { + #debug("ignoring spurious event, %?", event); + } + } + } + + #debug("%?", pkts[ready_packet]); + + for pkts.each |p| { p.unblock() } + + #debug("%?, %?", ready_packet, pkts[ready_packet]); + + assert pkts[ready_packet].state == full + || pkts[ready_packet].state == terminated; + + ready_packet +} + +#[doc = "Waits on a set of endpoints. Returns a message, its index, + and a list of the remaining endpoints."] +fn select(+endpoints: ~[recv_packet]) + -> (uint, option, ~[recv_packet]) +{ + let endpoints = vec::map_consume( + endpoints, + |p| unsafe { uniquify(p.unwrap()) }); + let endpoints_r = vec::view(endpoints, 0, endpoints.len()); + let ready = wait_many(endpoints_r.map_r(|p| &p.header)); + let mut remaining = ~[]; + let mut result = none; + do vec::consume(endpoints) |i, p| { + let p = recv_packet(unsafe { unsafe::transmute(p) }); + if i == ready { + result = recv(p); + } + else { + vec::push(remaining, p); + } + } + + (ready, result, remaining) +} + class send_packet { let mut p: option<*packet>; new(p: *packet) { - //#error("take send %?", p); + //#debug("take send %?", p); self.p = some(p); } drop { //if self.p != none { - // #error("drop send %?", option::get(self.p)); + // #debug("drop send %?", option::get(self.p)); //} if self.p != none { let mut p = none; @@ -196,12 +294,12 @@ class send_packet { class recv_packet { let mut p: option<*packet>; new(p: *packet) { - //#error("take recv %?", p); + //#debug("take recv %?", p); self.p = some(p); } drop { //if self.p != none { - // #error("drop recv %?", option::get(self.p)); + // #debug("drop recv %?", option::get(self.p)); //} if self.p != none { let mut p = none; @@ -222,7 +320,7 @@ fn entangle() -> (send_packet, recv_packet) { } fn spawn_service( - init: native fn() -> (send_packet, recv_packet), + init: extern fn() -> (send_packet, recv_packet), +service: fn~(+recv_packet)) -> send_packet { @@ -241,7 +339,7 @@ fn spawn_service( } fn spawn_service_recv( - init: native fn() -> (recv_packet, send_packet), + init: extern fn() -> (recv_packet, send_packet), +service: fn~(+send_packet)) -> recv_packet { diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index b3b9d089fea7a..eb3d52edc24e7 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -6,6 +6,7 @@ import libc::size_t; export append; export append_one; +export consume; export init_op; export is_empty; export is_not_empty; @@ -40,6 +41,7 @@ export grow_set; export map; export mapi; export map2; +export map_consume; export flat_map; export filter_map; export filter; @@ -261,8 +263,8 @@ pure fn slice(v: &[const T], start: uint, end: uint) -> ~[T] { ret result; } -/// Return a slice that points into another slice. -pure fn view(v: &[const T], start: uint, end: uint) -> &a.[T] { +#[doc = "Return a slice that points into another slice."] +pure fn view(v: &[const T], start: uint, end: uint) -> &a.[T] { assert (start <= end); assert (end <= len(v)); do unpack_slice(v) |p, _len| { @@ -373,7 +375,7 @@ fn rsplitn(v: &[T], n: uint, f: fn(T) -> bool) -> ~[~[T]] { /// Removes the first element from a vector and return it fn shift(&v: ~[T]) -> T { let ln = len::(v); - assert (ln > 0u); + assert (ln > 0); let mut vv = ~[]; v <-> vv; @@ -384,12 +386,12 @@ fn shift(&v: ~[T]) -> T { let vv = unsafe::to_ptr(vv); rr <- *vv; - for uint::range(1u, ln) |i| { + for uint::range(1, ln) |i| { let r <- *ptr::offset(vv, i); push(v, r); } } - unsafe::set_len(vv, 0u); + unsafe::set_len(vv, 0); rr } @@ -404,6 +406,17 @@ fn unshift(&v: ~[T], +x: T) { } } +fn consume(+v: ~[T], f: fn(uint, +T)) unsafe { + do unpack_slice(v) |p, ln| { + for uint::range(0, ln) |i| { + let x <- *ptr::offset(p, i); + f(i, x); + } + } + + unsafe::set_len(v, 0); +} + /// Remove the last element from a vector and return it fn pop(&v: ~[const T]) -> T { let ln = len(v); @@ -575,6 +588,14 @@ pure fn map(v: &[T], f: fn(T) -> U) -> ~[U] { ret result; } +fn map_consume(+v: ~[T], f: fn(+T) -> U) -> ~[U] { + let mut result = ~[]; + do consume(v) |_i, x| { + vec::push(result, f(x)); + } + result +} + /// Apply a function to each element of a vector and return the results pure fn mapi(v: &[T], f: fn(uint, T) -> U) -> ~[U] { let mut result = ~[]; @@ -1277,6 +1298,18 @@ impl extensions/& for &[T] { pure fn mapi(f: fn(uint, T) -> U) -> ~[U] { mapi(self, f) } + + #[inline] + fn map_r(f: fn(x: &self.T) -> U) -> ~[U] { + let mut r = ~[]; + let mut i = 0; + while i < self.len() { + push(r, f(&self[i])); + i += 1; + } + r + } + /** * Returns true if the function returns true for all elements. * diff --git a/src/test/bench/msgsend-ring-contracts.rs b/src/test/bench/msgsend-ring-contracts.rs index 99265353fe5de..9ad3025d33aed 100644 --- a/src/test/bench/msgsend-ring-contracts.rs +++ b/src/test/bench/msgsend-ring-contracts.rs @@ -119,7 +119,7 @@ fn main(args: [str]/~) { thread_ring(0u, msg_per_task, option::unwrap(num_chan), num_port); // synchronize - for futures.each |f| { f.get() }; + for futures.each |f| { future::get(f) }; let stop = time::precise_time_s(); diff --git a/src/test/run-pass/pipe-manual-2.rs b/src/test/run-pass/pipe-manual-2.rs index 0619a7b6b44a1..0803bbfe53170 100644 --- a/src/test/run-pass/pipe-manual-2.rs +++ b/src/test/run-pass/pipe-manual-2.rs @@ -13,184 +13,6 @@ At some point, we'll need to add support for select. */ -// Hopefully someday we'll move this into core. -mod pipes { - import unsafe::{forget, reinterpret_cast}; - - enum state { - empty, - full, - blocked, - terminated - } - - type packet = { - mut state: state, - mut blocked_task: option, - mut payload: option - }; - - fn packet() -> *packet unsafe { - let p: *packet = unsafe::transmute(~{ - mut state: empty, - mut blocked_task: none::, - mut payload: none:: - }); - p - } - - #[abi = "rust-intrinsic"] - native mod rusti { - fn atomic_xchng(&dst: int, src: int) -> int; - fn atomic_xchng_acq(&dst: int, src: int) -> int; - fn atomic_xchng_rel(&dst: int, src: int) -> int; - } - - // We should consider moving this to core::unsafe, although I - // suspect graydon would want us to use void pointers instead. - unsafe fn uniquify(x: *T) -> ~T { - unsafe { unsafe::reinterpret_cast(x) } - } - - fn swap_state_acq(&dst: state, src: state) -> state { - unsafe { - reinterpret_cast(rusti::atomic_xchng_acq( - *(ptr::mut_addr_of(dst) as *mut int), - src as int)) - } - } - - fn swap_state_rel(&dst: state, src: state) -> state { - unsafe { - reinterpret_cast(rusti::atomic_xchng_rel( - *(ptr::mut_addr_of(dst) as *mut int), - src as int)) - } - } - - fn send(-p: send_packet, -payload: T) { - let p = p.unwrap(); - let p = unsafe { uniquify(p) }; - assert (*p).payload == none; - (*p).payload <- some(payload); - let old_state = swap_state_rel((*p).state, full); - alt old_state { - empty { - // Yay, fastpath. - - // The receiver will eventually clean this up. - unsafe { forget(p); } - } - full { fail "duplicate send" } - blocked { - // FIXME: once the target will actually block, tell the - // scheduler to wake it up. - - // The receiver will eventually clean this up. - unsafe { forget(p); } - } - terminated { - // The receiver will never receive this. Rely on drop_glue - // to clean everything up. - } - } - } - - fn recv(-p: recv_packet) -> option { - let p = p.unwrap(); - let p = unsafe { uniquify(p) }; - loop { - let old_state = swap_state_acq((*p).state, - blocked); - alt old_state { - empty | blocked { task::yield(); } - full { - let mut payload = none; - payload <-> (*p).payload; - ret some(option::unwrap(payload)) - } - terminated { - assert old_state == terminated; - ret none; - } - } - } - } - - fn sender_terminate(p: *packet) { - let p = unsafe { uniquify(p) }; - alt swap_state_rel((*p).state, terminated) { - empty | blocked { - // The receiver will eventually clean up. - unsafe { forget(p) } - } - full { - // This is impossible - fail "you dun goofed" - } - terminated { - // I have to clean up, use drop_glue - } - } - } - - fn receiver_terminate(p: *packet) { - let p = unsafe { uniquify(p) }; - alt swap_state_rel((*p).state, terminated) { - empty { - // the sender will clean up - unsafe { forget(p) } - } - blocked { - // this shouldn't happen. - fail "terminating a blocked packet" - } - terminated | full { - // I have to clean up, use drop_glue - } - } - } - - class send_packet { - let mut p: option<*packet>; - new(p: *packet) { self.p = some(p); } - drop { - if self.p != none { - let mut p = none; - p <-> self.p; - sender_terminate(option::unwrap(p)) - } - } - fn unwrap() -> *packet { - let mut p = none; - p <-> self.p; - option::unwrap(p) - } - } - - class recv_packet { - let mut p: option<*packet>; - new(p: *packet) { self.p = some(p); } - drop { - if self.p != none { - let mut p = none; - p <-> self.p; - receiver_terminate(option::unwrap(p)) - } - } - fn unwrap() -> *packet { - let mut p = none; - p <-> self.p; - option::unwrap(p) - } - } - - fn entangle() -> (send_packet, recv_packet) { - let p = packet(); - (send_packet(p), recv_packet(p)) - } -} - mod pingpong { enum ping = *pipes::packet; enum pong = *pipes::packet; diff --git a/src/test/run-pass/pipe-manual-3.rs b/src/test/run-pass/pipe-manual-3.rs index 905063ea713cb..9f05038aadc6c 100644 --- a/src/test/run-pass/pipe-manual-3.rs +++ b/src/test/run-pass/pipe-manual-3.rs @@ -15,184 +15,6 @@ This file does horrible things to pretend we have self-move. */ -// Hopefully someday we'll move this into core. -mod pipes { - import unsafe::{forget, reinterpret_cast}; - - enum state { - empty, - full, - blocked, - terminated - } - - type packet = { - mut state: state, - mut blocked_task: option, - mut payload: option - }; - - fn packet() -> *packet unsafe { - let p: *packet = unsafe::transmute(~{ - mut state: empty, - mut blocked_task: none::, - mut payload: none:: - }); - p - } - - #[abi = "rust-intrinsic"] - native mod rusti { - fn atomic_xchng(&dst: int, src: int) -> int; - fn atomic_xchng_acq(&dst: int, src: int) -> int; - fn atomic_xchng_rel(&dst: int, src: int) -> int; - } - - // We should consider moving this to core::unsafe, although I - // suspect graydon would want us to use void pointers instead. - unsafe fn uniquify(x: *T) -> ~T { - unsafe { unsafe::reinterpret_cast(x) } - } - - fn swap_state_acq(&dst: state, src: state) -> state { - unsafe { - reinterpret_cast(rusti::atomic_xchng_acq( - *(ptr::mut_addr_of(dst) as *mut int), - src as int)) - } - } - - fn swap_state_rel(&dst: state, src: state) -> state { - unsafe { - reinterpret_cast(rusti::atomic_xchng_rel( - *(ptr::mut_addr_of(dst) as *mut int), - src as int)) - } - } - - fn send(-p: send_packet, -payload: T) { - let p = p.unwrap(); - let p = unsafe { uniquify(p) }; - assert (*p).payload == none; - (*p).payload <- some(payload); - let old_state = swap_state_rel((*p).state, full); - alt old_state { - empty { - // Yay, fastpath. - - // The receiver will eventually clean this up. - unsafe { forget(p); } - } - full { fail "duplicate send" } - blocked { - // FIXME: once the target will actually block, tell the - // scheduler to wake it up. - - // The receiver will eventually clean this up. - unsafe { forget(p); } - } - terminated { - // The receiver will never receive this. Rely on drop_glue - // to clean everything up. - } - } - } - - fn recv(-p: recv_packet) -> option { - let p = p.unwrap(); - let p = unsafe { uniquify(p) }; - loop { - let old_state = swap_state_acq((*p).state, - blocked); - alt old_state { - empty | blocked { task::yield(); } - full { - let mut payload = none; - payload <-> (*p).payload; - ret some(option::unwrap(payload)) - } - terminated { - assert old_state == terminated; - ret none; - } - } - } - } - - fn sender_terminate(p: *packet) { - let p = unsafe { uniquify(p) }; - alt swap_state_rel((*p).state, terminated) { - empty | blocked { - // The receiver will eventually clean up. - unsafe { forget(p) } - } - full { - // This is impossible - fail "you dun goofed" - } - terminated { - // I have to clean up, use drop_glue - } - } - } - - fn receiver_terminate(p: *packet) { - let p = unsafe { uniquify(p) }; - alt swap_state_rel((*p).state, terminated) { - empty { - // the sender will clean up - unsafe { forget(p) } - } - blocked { - // this shouldn't happen. - fail "terminating a blocked packet" - } - terminated | full { - // I have to clean up, use drop_glue - } - } - } - - class send_packet { - let mut p: option<*packet>; - new(p: *packet) { self.p = some(p); } - drop { - if self.p != none { - let mut p = none; - p <-> self.p; - sender_terminate(option::unwrap(p)) - } - } - fn unwrap() -> *packet { - let mut p = none; - p <-> self.p; - option::unwrap(p) - } - } - - class recv_packet { - let mut p: option<*packet>; - new(p: *packet) { self.p = some(p); } - drop { - if self.p != none { - let mut p = none; - p <-> self.p; - receiver_terminate(option::unwrap(p)) - } - } - fn unwrap() -> *packet { - let mut p = none; - p <-> self.p; - option::unwrap(p) - } - } - - fn entangle() -> (send_packet, recv_packet) { - let p = packet(); - (send_packet(p), recv_packet(p)) - } -} - mod pingpong { enum ping { ping, } enum ping_message = *pipes::packet; diff --git a/src/test/run-pass/pipe-select.rs b/src/test/run-pass/pipe-select.rs new file mode 100644 index 0000000000000..163cfa954884a --- /dev/null +++ b/src/test/run-pass/pipe-select.rs @@ -0,0 +1,125 @@ +use std; +import std::timer::sleep; +import std::uv; + +import pipes::{recv, select}; + +// Compiled by pipec +mod oneshot { + fn init() -> (client::waiting, server::waiting) { pipes::entangle() } + enum waiting { signal(server::signaled), } + enum signaled { } + mod client { + fn signal(-pipe: waiting) -> signaled { + let (c, s) = pipes::entangle(); + let message = oneshot::signal(s); + pipes::send(pipe, message); + c + } + type waiting = pipes::send_packet; + type signaled = pipes::send_packet; + } + mod server { + impl recv for waiting { + fn recv() -> extern fn(-waiting) -> oneshot::waiting { + fn recv(-pipe: waiting) -> oneshot::waiting { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type waiting = pipes::recv_packet; + impl recv for signaled { + fn recv() -> extern fn(-signaled) -> oneshot::signaled { + fn recv(-pipe: signaled) -> oneshot::signaled { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type signaled = pipes::recv_packet; + } +} + +mod stream { + fn init() -> (client::stream, server::stream) { + pipes::entangle() + } + enum stream { send(T, server::stream), } + mod client { + fn send(+pipe: stream, +x_0: T) -> stream { + { + let (c, s) = pipes::entangle(); + let message = stream::send(x_0, s); + pipes::send(pipe, message); + c + } + } + type stream = pipes::send_packet>; + } + mod server { + impl recv for stream { + fn recv() -> extern fn(+stream) -> stream::stream { + fn recv(+pipe: stream) -> stream::stream { + option::unwrap(pipes::recv(pipe)) + } + recv + } + } + type stream = pipes::recv_packet>; + } +} + +fn main() { + import oneshot::client::*; + import stream::client::*; + + let iotask = uv::global_loop::get(); + + #macro[ + [#recv[chan], + chan.recv()(chan)] + ]; + + let c = pipes::spawn_service(stream::init, |p| { + #error("waiting for pipes"); + let stream::send(x, p) = option::unwrap(recv(p)); + #error("got pipes"); + let (left, right) : (oneshot::server::waiting, + oneshot::server::waiting) + = x; + #error("selecting"); + let (i, _, _) = select(~[left, right]); + #error("selected"); + assert i == 0; + + #error("waiting for pipes"); + let stream::send(x, _) = option::unwrap(recv(p)); + #error("got pipes"); + let (left, right) : (oneshot::server::waiting, + oneshot::server::waiting) + = x; + #error("selecting"); + let (i, _, _) = select(~[left, right]); + #error("selected"); + assert i == 1; + }); + + let (c1, p1) = oneshot::init(); + let (c2, p2) = oneshot::init(); + + let c = send(c, (p1, p2)); + + sleep(iotask, 1000); + + signal(c1); + + let (c1, p1) = oneshot::init(); + let (c2, p2) = oneshot::init(); + + send(c, (p1, p2)); + + sleep(iotask, 1000); + + signal(c2); +} \ No newline at end of file From 05cdda3a2c147fdc8caef4e6c662f518ec325e0a Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 5 Jul 2012 12:10:33 -0700 Subject: [PATCH 14/52] Plumbing and parsing for item-position macros. --- src/libsyntax/ast.rs | 1 + src/libsyntax/ext/base.rs | 13 +++++- src/libsyntax/ext/expand.rs | 42 +++++++++++++++++-- src/libsyntax/ext/pipes.rs | 10 +++++ src/libsyntax/fold.rs | 4 ++ src/libsyntax/parse.rs | 6 ++- src/libsyntax/parse/attr.rs | 1 - src/libsyntax/parse/common.rs | 9 ++++ src/libsyntax/parse/parser.rs | 17 +++++++- src/libsyntax/print/pprust.rs | 3 ++ src/libsyntax/syntax.rc | 2 + src/libsyntax/visit.rs | 1 + src/rustc/metadata/encoder.rs | 2 + src/rustc/middle/resolve.rs | 4 ++ src/rustc/middle/resolve3.rs | 10 ++++- src/rustc/middle/trans/reachable.rs | 1 + .../middle/tstate/pre_post_conditions.rs | 1 + src/rustc/middle/typeck/collect.rs | 1 + src/test/run-pass/pipe-pingpong-proto.rs | 17 ++++++++ 19 files changed, 135 insertions(+), 10 deletions(-) create mode 100644 src/libsyntax/ext/pipes.rs create mode 100644 src/test/run-pass/pipe-pingpong-proto.rs diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index a817faad0691e..4c0be731df4ba 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -704,6 +704,7 @@ enum item_ { item_trait(~[ty_param], region_param, ~[ty_method]), item_impl(~[ty_param], region_param, option<@trait_ref> /* trait */, @ty /* self */, ~[@method]), + item_mac(mac), } #[auto_serialize] diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 28e5c2c5f171c..0e6cce27b2b85 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -17,12 +17,18 @@ type item_decorator = type syntax_expander_tt = {expander: syntax_expander_tt_, span: option}; type syntax_expander_tt_ = fn@(ext_ctxt, span, ast::token_tree) -> @ast::expr; +type syntax_expander_tt_item + = {expander: syntax_expander_tt_item_, span: option}; +type syntax_expander_tt_item_ + = fn@(ext_ctxt, span, ast::ident, ast::token_tree) -> @ast::item; + enum syntax_extension { normal(syntax_expander), macro_defining(macro_definer), item_decorator(item_decorator), - normal_tt(syntax_expander_tt) + normal_tt(syntax_expander_tt), + item_tt(syntax_expander_tt_item), } // A temporary hard-coded map of methods for expanding syntax extension @@ -30,6 +36,9 @@ enum syntax_extension { fn syntax_expander_table() -> hashmap { fn builtin(f: syntax_expander_) -> syntax_extension {normal({expander: f, span: none})} + fn builtin_item_tt(f: syntax_expander_tt_item_) -> syntax_extension { + item_tt({expander: f, span: none}) + } let syntax_expanders = str_hash::(); syntax_expanders.insert("fmt", builtin(ext::fmt::expand_syntax_ext)); syntax_expanders.insert("auto_serialize", @@ -61,6 +70,8 @@ fn syntax_expander_table() -> hashmap { builtin(ext::source_util::expand_include_bin)); syntax_expanders.insert("mod", builtin(ext::source_util::expand_mod)); + syntax_expanders.insert("proto", + builtin_item_tt(ext::pipes::expand_proto)); ret syntax_expanders; } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index a037d87166ae9..63b3c0881e14f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1,7 +1,7 @@ import std::map::hashmap; import ast::{crate, expr_, expr_mac, mac_invoc, mac_invoc_tt, - tt_delim, tt_flat}; + tt_delim, tt_flat, item_mac}; import fold::*; import ext::base::*; import ext::qquote::{qq_helper}; @@ -52,6 +52,10 @@ fn expand_expr(exts: hashmap, cx: ext_ctxt, #fmt["this tt-style macro should be \ invoked '%s!{...}'", *extname]) } + some(item_tt(*)) { + cx.span_fatal(pth.span, + "cannot use item macros in this context"); + } } } mac_invoc_tt(pth, tt) { @@ -109,7 +113,7 @@ fn expand_mod_items(exts: hashmap, cx: ext_ctxt, }; alt exts.find(*mname) { none | some(normal(_)) | some(macro_defining(_)) - | some(normal_tt(_)) { + | some(normal_tt(_)) | some(item_tt(*)) { items } @@ -124,7 +128,8 @@ fn expand_mod_items(exts: hashmap, cx: ext_ctxt, } /* record module we enter for `#mod` */ -fn expand_item(cx: ext_ctxt, &&it: @ast::item, fld: ast_fold, +fn expand_item(exts: hashmap, + cx: ext_ctxt, &&it: @ast::item, fld: ast_fold, orig: fn@(&&@ast::item, ast_fold) -> @ast::item) -> @ast::item { @@ -132,12 +137,41 @@ fn expand_item(cx: ext_ctxt, &&it: @ast::item, fld: ast_fold, ast::item_mod(_) | ast::item_foreign_mod(_) {true} _ {false} }; + let it = alt it.node { + ast::item_mac(*) { + expand_item_mac(exts, cx, it) + } + _ { it } + }; if is_mod { cx.mod_push(it.ident); } let ret_val = orig(it, fld); if is_mod { cx.mod_pop(); } ret ret_val; } +fn expand_item_mac(exts: hashmap, + cx: ext_ctxt, &&it: @ast::item) -> @ast::item { + alt it.node { + item_mac({node: mac_invoc_tt(pth, tt), span}) { + let extname = pth.idents[0]; + alt exts.find(*extname) { + none { + cx.span_fatal(pth.span, + #fmt("macro undefined: '%s'", *extname)) + } + some(item_tt(expand)) { + expand.expander(cx, it.span, it.ident, tt) + } + _ { cx.span_fatal(it.span, + #fmt("%s is not a legal here", *extname)) } + } + } + _ { + cx.span_bug(it.span, "invalid item macro invocation"); + } + } +} + fn new_span(cx: ext_ctxt, sp: span) -> span { /* this discards information in the case of macro-defining macros */ ret {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()}; @@ -166,7 +200,7 @@ fn expand_crate(parse_sess: parse::parse_sess, let f_pre = @{fold_expr: |a,b,c| expand_expr(exts, cx, a, b, c, afp.fold_expr), fold_mod: |a,b| expand_mod_items(exts, cx, a, b, afp.fold_mod), - fold_item: |a,b| expand_item(cx, a, b, afp.fold_item), + fold_item: |a,b| expand_item(exts, cx, a, b, afp.fold_item), new_span: |a|new_span(cx, a) with *afp}; let f = make_fold(f_pre); diff --git a/src/libsyntax/ext/pipes.rs b/src/libsyntax/ext/pipes.rs new file mode 100644 index 0000000000000..23bd95226c37c --- /dev/null +++ b/src/libsyntax/ext/pipes.rs @@ -0,0 +1,10 @@ + +import codemap::span; +import ext::base::ext_ctxt; + +fn expand_proto(cx: ext_ctxt, span: span, id: ast::ident, tt: ast::token_tree) + -> @ast::item +{ + cx.span_unimpl(span, + "Protocol compiler") +} \ No newline at end of file diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index ad55c85496b3a..75977ba616931 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -283,6 +283,10 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ { rp, /* FIXME (#2543) */ copy methods) } + item_mac(m) { + // TODO: we might actually want to do something here. + item_mac(m) + } }; } diff --git a/src/libsyntax/parse.rs b/src/libsyntax/parse.rs index 9c143257e9eae..b2d06311e673a 100644 --- a/src/libsyntax/parse.rs +++ b/src/libsyntax/parse.rs @@ -12,6 +12,9 @@ export parse_crate_from_source_str; export parse_expr_from_source_str, parse_item_from_source_str; export parse_from_source_str; +// this used to be `import common::parser_common`, but it was causing +// unresolved import errors. Maybe resolve3 will fix it. +import common::*; import parser::parser; //import attr::parser_attr; import attr::*; //resolve bug? @@ -20,8 +23,7 @@ import common::*; //resolve bug? import ast::node_id; import util::interner; // FIXME (#1935): resolve badness -import lexer::{string_reader_as_reader, tt_reader_as_reader, reader, - string_reader, tt_reader}; +import lexer::*; import diagnostic::{span_handler, mk_span_handler, mk_handler, emitter}; type parse_sess = @{ diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index d7ae4995520ec..e62de46f5dbf1 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -1,7 +1,6 @@ import either::{either, left, right}; import ast_util::spanned; import common::*; //resolve bug? -//import common::{parser_common, seq_sep_trailing_disallowed}; export attr_or_ext; export parser_attr; diff --git a/src/libsyntax/parse/common.rs b/src/libsyntax/parse/common.rs index d4331ee766f8b..16059b473bbb6 100644 --- a/src/libsyntax/parse/common.rs +++ b/src/libsyntax/parse/common.rs @@ -92,6 +92,15 @@ impl parser_common for parser { self.token_is_keyword(word, self.token) } + fn is_any_keyword(tok: token::token) -> bool { + alt tok { + token::IDENT(sid, false) { + self.keywords.contains_key(*self.get_str(sid)) + } + _ { false } + } + } + fn eat_keyword(word: str) -> bool { self.require_keyword(word); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8bac3e0d751e7..2f4fe783b4903 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -9,7 +9,7 @@ import lexer::reader; import prec::{as_prec, token_to_binop}; import attr::parser_attr; import common::{seq_sep_trailing_disallowed, seq_sep_trailing_allowed, - seq_sep_none, token_to_str, parser_common}; + seq_sep_none, token_to_str}; import dvec::{dvec, extensions}; import vec::{push}; import ast::*; @@ -2595,6 +2595,21 @@ class parser { self.parse_item_impl() } else if self.eat_keyword("class") { self.parse_item_class() + } else if !self.is_any_keyword(copy self.token) + && self.look_ahead(1) == token::NOT + { + // item macro. + let pth = self.parse_path_without_tps(); + #error("parsing invocation of %s", *pth.idents[0]); + self.expect(token::NOT); + let id = self.parse_ident(); + let tt = self.parse_token_tree(); + let m = ast::mac_invoc_tt(pth, tt); + let m: ast::mac = {node: m, + span: {lo: self.span.lo, + hi: self.span.hi, + expn_info: none}}; + (id, item_mac(m), none) } else { ret none; }; some(self.mk_item(lo, self.last_span.hi, ident, item_, vis, alt extra_attrs { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 8e85de17613c4..5220ecdce9363 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -589,6 +589,9 @@ fn print_item(s: ps, &&item: @ast::item) { for methods.each |meth| { print_ty_method(s, meth); } bclose(s, item.span); } + ast::item_mac(_m) { + fail "item macros unimplemented" + } } s.ann.post(ann_node); } diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc index 9cf5528e1a34e..5a384f04cb338 100644 --- a/src/libsyntax/syntax.rc +++ b/src/libsyntax/syntax.rc @@ -78,4 +78,6 @@ mod ext { mod log_syntax; mod auto_serialize; mod source_util; + + mod pipes; } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 25c61535fcf8b..ed54ad3308b0b 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -163,6 +163,7 @@ fn visit_item(i: @item, e: E, v: vt) { v.visit_ty(m.decl.output, e, v); } } + item_mac(_m) { fail "item macros unimplemented" } } } diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 8da5ac9420fe1..46a3cd84be3ba 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -217,6 +217,7 @@ fn encode_module_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt, } } item_impl(*) {} + item_mac(*) { fail "item macros unimplemented" } } } } @@ -749,6 +750,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, encode_path(ebml_w, path, ast_map::path_name(item.ident)); ebml_w.end_tag(); } + item_mac(*) { fail "item macros unimplemented" } } } diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 51c48f6a64c04..d8b1f4db64010 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -1352,6 +1352,7 @@ fn found_def_item(i: @ast::item, ns: namespace) -> option { } } ast::item_impl(*) { /* ??? */ } + ast::item_mac(*) { fail "item macros unimplemented" } } ret none; } @@ -1658,6 +1659,9 @@ fn index_mod(md: ast::_mod) -> mod_index { // add the class name itself add_to_index(index, it.ident, mie_item(it)); } + ast::item_mac(*) { + fail "item macros unimplemented" + } } } ret index; diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs index b7dc314c801c1..6ca613d528103 100644 --- a/src/rustc/middle/resolve3.rs +++ b/src/rustc/middle/resolve3.rs @@ -13,7 +13,7 @@ import syntax::ast::{expr_binary, expr_cast, expr_field, expr_fn}; import syntax::ast::{expr_fn_block, expr_index, expr_new, expr_path}; import syntax::ast::{expr_unary, fn_decl, foreign_item, foreign_item_fn}; import syntax::ast::{ident, trait_ref, impure_fn, instance_var, item}; -import syntax::ast::{item_class, item_const, item_enum, item_fn}; +import syntax::ast::{item_class, item_const, item_enum, item_fn, item_mac}; import syntax::ast::{item_foreign_mod, item_trait, item_impl, item_mod}; import syntax::ast::{item_ty, local, local_crate, method, node_id, pat}; import syntax::ast::{pat_enum, pat_ident, path, prim_ty, stmt_decl, ty}; @@ -871,6 +871,10 @@ class Resolver { (*name_bindings).define_type(def_ty(local_def(item.id))); visit_item(item, new_parent, visitor); } + + item_mac(*) { + fail "item macros unimplemented" + } } } @@ -2854,6 +2858,10 @@ class Resolver { item_const(*) { visit_item(item, (), visitor); } + + item_mac(*) { + fail "item macros unimplemented" + } } self.xray_context = orig_xray_flag; diff --git a/src/rustc/middle/trans/reachable.rs b/src/rustc/middle/trans/reachable.rs index edf76f4230396..f9b031f1a51df 100644 --- a/src/rustc/middle/trans/reachable.rs +++ b/src/rustc/middle/trans/reachable.rs @@ -148,6 +148,7 @@ fn traverse_public_item(cx: ctx, item: @item) { } item_const(*) | item_enum(*) | item_trait(*) {} + item_mac(*) { fail "item macros unimplemented" } } } diff --git a/src/rustc/middle/tstate/pre_post_conditions.rs b/src/rustc/middle/tstate/pre_post_conditions.rs index 0561a04544a6e..545f6ad6aa97c 100644 --- a/src/rustc/middle/tstate/pre_post_conditions.rs +++ b/src/rustc/middle/tstate/pre_post_conditions.rs @@ -55,6 +55,7 @@ fn find_pre_post_item(ccx: crate_ctxt, i: item) { item_impl(_, _, _, _, ms) { for ms.each |m| { find_pre_post_method(ccx, m); } } + item_mac(*) { fail "item macros unimplemented" } } } diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index 84ed98be6f4b2..4bae90d22144b 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -537,6 +537,7 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) } ast::item_impl(*) | ast::item_mod(_) | ast::item_foreign_mod(_) { fail; } + ast::item_mac(*) { fail "item macros unimplemented" } } } diff --git a/src/test/run-pass/pipe-pingpong-proto.rs b/src/test/run-pass/pipe-pingpong-proto.rs new file mode 100644 index 0000000000000..0afacb99c1596 --- /dev/null +++ b/src/test/run-pass/pipe-pingpong-proto.rs @@ -0,0 +1,17 @@ +// xfail-test + +// An example to make sure the protocol parsing syntax extension works. + +proto! pingpong { + ping:send { + ping -> pong + } + + pong:recv { + pong -> ping + } +} + +fn main() { + // TODO: do something with the protocol +} \ No newline at end of file From f0ef4ef81b26388261ae56fdb019982ed3141668 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 5 Jul 2012 16:07:53 -0700 Subject: [PATCH 15/52] You can have any protocol you want, provided it's pingpong. This integrates the pipe compiler into the proto syntax extension. --- src/libsyntax/ext/pipes.rs | 11 +- src/libsyntax/ext/pipes/ast_builder.rs | 182 +++++++++++ src/libsyntax/ext/pipes/pipec.rs | 392 +++++++++++++++++++++++ src/libsyntax/syntax.rc | 5 +- src/test/run-pass/pipe-pingpong-proto.rs | 44 ++- 5 files changed, 627 insertions(+), 7 deletions(-) create mode 100644 src/libsyntax/ext/pipes/ast_builder.rs create mode 100644 src/libsyntax/ext/pipes/pipec.rs diff --git a/src/libsyntax/ext/pipes.rs b/src/libsyntax/ext/pipes.rs index 23bd95226c37c..5a84d764b9f1f 100644 --- a/src/libsyntax/ext/pipes.rs +++ b/src/libsyntax/ext/pipes.rs @@ -2,9 +2,16 @@ import codemap::span; import ext::base::ext_ctxt; +import pipes::pipec::*; + fn expand_proto(cx: ext_ctxt, span: span, id: ast::ident, tt: ast::token_tree) -> @ast::item { - cx.span_unimpl(span, - "Protocol compiler") + let proto = protocol(id); + let ping = proto.add_state(@"ping", send); + let pong = proto.add_state(@"pong", recv); + + ping.add_message(@"ping", []/~, pong, ~[]); + pong.add_message(@"pong", []/~, ping, ~[]); + proto.compile(cx) } \ No newline at end of file diff --git a/src/libsyntax/ext/pipes/ast_builder.rs b/src/libsyntax/ext/pipes/ast_builder.rs new file mode 100644 index 0000000000000..b23e707f4eb2c --- /dev/null +++ b/src/libsyntax/ext/pipes/ast_builder.rs @@ -0,0 +1,182 @@ +// Functions for building ASTs, without having to fuss with spans. +// +// To start with, it will be use dummy spans, but it might someday do +// something smarter. + +import ast::{ident, node_id}; +import codemap::span; +import ext::base::mk_ctxt; + +fn ident(s: str) -> ast::ident { + @(copy s) +} + +fn empty_span() -> span { + {lo: 0, hi: 0, expn_info: none} +} + +fn span(+x: T) -> ast::spanned { + {node: x, + span: empty_span()} +} + +fn path(id: ident) -> @ast::path { + @{span: empty_span(), + global: false, + idents: ~[id], + rp: none, + types: ~[]} +} + +impl methods for ident { + fn +(id: ident) -> @ast::path { + path(self) + id + } +} + +impl methods for @ast::path { + fn +(id: ident) -> @ast::path { + @{idents: vec::append_one(self.idents, id) + with *self} + } + + fn add_ty(ty: @ast::ty) -> @ast::path { + @{types: vec::append_one(self.types, ty) + with *self} + } + + fn add_tys(+tys: ~[@ast::ty]) -> @ast::path { + @{types: vec::append(self.types, tys) + with *self} + } +} + +impl ast_builder for ext_ctxt { + fn ty_param(id: ast::ident, +bounds: ~[ast::ty_param_bound]) + -> ast::ty_param + { + {ident: id, id: self.next_id(), bounds: @bounds} + } + + fn arg(name: ident, ty: @ast::ty) -> ast::arg { + {mode: ast::infer(self.next_id()), + ty: ty, + ident: name, + // TODO: should this be the same as the infer id? + id: self.next_id()} + } + + fn arg_mode(name: ident, ty: @ast::ty, mode: ast::rmode) -> ast::arg { + {mode: ast::expl(mode), + ty: ty, + ident: name, + id: self.next_id()} + } + + fn expr_block(e: @ast::expr) -> ast::blk { + let blk = {view_items: ~[], + stmts: ~[], + expr: some(e), + id: self.next_id(), + rules: ast::default_blk}; + + {node: blk, + span: empty_span()} + } + + fn fn_decl(+inputs: ~[ast::arg], + output: @ast::ty) -> ast::fn_decl { + {inputs: inputs, + output: output, + purity: ast::impure_fn, + cf: ast::return_val, + // TODO: we'll probably want a variant that does constrained + // types. + constraints: ~[]} + } + + fn item(name: ident, + +node: ast::item_) -> @ast::item { + @{ident: name, + attrs: ~[], + id: self.next_id(), + node: node, + vis: ast::public, + span: empty_span()} + } + + fn item_fn_poly(name: ident, + +inputs: ~[ast::arg], + output: @ast::ty, + +ty_params: ~[ast::ty_param], + +body: ast::blk) -> @ast::item { + self.item(name, + ast::item_fn(self.fn_decl(inputs, output), + ty_params, + body)) + } + + fn item_fn(name: ident, + +inputs: ~[ast::arg], + output: @ast::ty, + +body: ast::blk) -> @ast::item { + self.item_fn_poly(name, inputs, output, ~[], body) + } + + fn item_enum_poly(name: ident, + +variants: ~[ast::variant], + +ty_params: ~[ast::ty_param]) -> @ast::item { + self.item(name, + ast::item_enum(variants, + ty_params, + ast::rp_none)) + } + + fn item_enum(name: ident, + +variants: ~[ast::variant]) -> @ast::item { + self.item_enum_poly(name, variants, ~[]) + } + + fn variant(name: ident, + +tys: ~[@ast::ty]) -> ast::variant { + let args = tys.map(|ty| {ty: ty, id: self.next_id()}); + + span({name: name, + attrs: ~[], + args: args, + id: self.next_id(), + disr_expr: none, + vis: ast::public}) + } + + fn item_mod(name: ident, + +items: ~[@ast::item]) -> @ast::item { + self.item(name, + ast::item_mod({ + view_items: ~[], + items: items})) + } + + fn ty_path(path: @ast::path) -> @ast::ty { + // TODO: make sure the node ids are legal. + @{id: self.next_id(), + node: ast::ty_path(path, self.next_id()), + span: empty_span()} + } + + fn item_ty_poly(name: ident, + ty: @ast::ty, + +params: ~[ast::ty_param]) -> @ast::item { + self.item(name, + ast::item_ty(ty, params, ast::rp_none)) + } + + fn item_ty(name: ident, + ty: @ast::ty) -> @ast::item { + self.item_ty_poly(name, ty, ~[]) + } + + fn ty_vars(+ty_params: ~[ast::ty_param]) -> ~[@ast::ty] { + ty_params.map(|p| self.ty_path(path(p.ident))) + } +} diff --git a/src/libsyntax/ext/pipes/pipec.rs b/src/libsyntax/ext/pipes/pipec.rs new file mode 100644 index 0000000000000..0ff4ba92b3663 --- /dev/null +++ b/src/libsyntax/ext/pipes/pipec.rs @@ -0,0 +1,392 @@ +// A protocol compiler for Rust. + +import to_str::to_str; + +import dvec::dvec; +import dvec::extensions; + +import ast::ident; +import util::interner; +import interner::{intern, get}; +import print::pprust; +import pprust::{item_to_str, ty_to_str}; +import ext::base::{mk_ctxt, ext_ctxt}; +import parse; +import parse::{parse_item_from_source_str}; + +import ast_builder::ast_builder; +import ast_builder::methods; +import ast_builder::path; + +enum direction { + send, recv +} + +impl of to_str for direction { + fn to_str() -> str { + alt self { + send { "send" } + recv { "recv" } + } + } +} + +impl methods for direction { + fn reverse() -> direction { + alt self { + send { recv } + recv { send } + } + } +} + +enum message { + // name, data, current state, next state, next tys + message(ident, ~[@ast::ty], state, state, ~[@ast::ty]) +} + +impl methods for message { + fn name() -> ident { + alt self { + message(id, _, _, _, _) { + id + } + } + } + + // Return the type parameters actually used by this message + fn get_params() -> ~[ast::ty_param] { + let mut used = ~[]; + alt self { + message(_, tys, this, _, next_tys) { + let parms = this.ty_params; + for vec::append(tys, next_tys).each |ty| { + alt ty.node { + ast::ty_path(path, _) { + if path.idents.len() == 1 { + let id = path.idents[0]; + + let found = parms.find(|p| id == p.ident); + + alt found { + some(p) { + if !used.contains(p) { + vec::push(used, p); + } + } + none { } + } + } + } + _ { } + } + } + } + } + used + } + + fn gen_send(cx: ext_ctxt) -> @ast::item { + alt self { + message(id, tys, this, next, next_tys) { + let arg_names = tys.mapi(|i, _ty| @("x_" + i.to_str())); + + let args = (arg_names, tys).map(|n, t| + *n + ": " + t.to_source()); + + let args_ast = (arg_names, tys).map( + |n, t| cx.arg_mode(n, t, ast::by_copy) + ); + + let args_ast = vec::append( + ~[cx.arg_mode(@"pipe", + cx.ty_path(path(this.data_name()) + .add_tys(cx.ty_vars(this.ty_params))), + ast::by_copy)], + args_ast); + + let args = [#fmt("-pipe: %s", *this.data_name())]/~ + args; + + let pat = alt (this.dir, next.dir) { + (send, send) { "(c, s)" } + (send, recv) { "(s, c)" } + (recv, send) { "(s, c)" } + (recv, recv) { "(c, s)" } + }; + + let mut body = #fmt("{ let %s = pipes::entangle();\n", pat); + body += #fmt("let message = %s::%s(%s);\n", + *this.proto.name, + *self.name(), + str::connect(vec::append_one(arg_names, @"s") + .map(|x| *x), + ", ")); + body += #fmt("pipes::send(pipe, message);\n"); + body += "c }"; + + let body = cx.parse_expr(body); + + cx.item_fn_poly(self.name(), + args_ast, + cx.ty_path(path(next.data_name()) + .add_tys(next_tys)), + self.get_params(), + cx.expr_block(body)) + } + } + } +} + +enum state { + state_(@{ + name: ident, + dir: direction, + ty_params: ~[ast::ty_param], + messages: dvec, + proto: protocol, + }), +} + +impl methods for state { + fn add_message(name: ident, +data: ~[@ast::ty], next: state, + +next_tys: ~[@ast::ty]) { + assert next_tys.len() == next.ty_params.len(); + self.messages.push(message(name, data, self, next, next_tys)); + } + + fn filename() -> str { + (*self).proto.filename() + } + + fn data_name() -> ident { + self.name + } + + fn to_ty(cx: ext_ctxt) -> @ast::ty { + cx.ty_path(path(self.name).add_tys(cx.ty_vars(self.ty_params))) + } + + fn to_type_decls(cx: ext_ctxt) -> [@ast::item]/~ { + // This compiles into two different type declarations. Say the + // state is called ping. This will generate both `ping` and + // `ping_message`. The first contains data that the user cares + // about. The second is the same thing, but extended with a + // next packet pointer, which is used under the covers. + + let name = self.data_name(); + + let mut items_msg = []/~; + + for self.messages.each |m| { + let message(_, tys, this, next, next_tys) = m; + + let name = m.name(); + let next_name = next.data_name(); + + let dir = alt this.dir { + send { @"server" } + recv { @"client" } + }; + + let v = cx.variant(name, + vec::append_one( + tys, + cx.ty_path((dir + next_name) + .add_tys(next_tys)))); + + vec::push(items_msg, v); + } + + ~[cx.item_enum_poly(name, items_msg, self.ty_params)] + } + + fn to_endpoint_decls(cx: ext_ctxt, dir: direction) -> [@ast::item]/~ { + let dir = alt dir { + send { (*self).dir } + recv { (*self).dir.reverse() } + }; + let mut items = ~[]; + for self.messages.each |m| { + if dir == send { + vec::push(items, m.gen_send(cx)) + } + } + + vec::push(items, + cx.item_ty_poly( + self.data_name(), + cx.ty_path( + (@"pipes" + @(dir.to_str() + "_packet")) + .add_ty(cx.ty_path( + (self.proto.name + self.data_name()) + .add_tys(cx.ty_vars(self.ty_params))))), + self.ty_params)); + items + } +} + +enum protocol { + protocol_(@{ + name: ident, + states: dvec, + }), +} + +fn protocol(name: ident) -> protocol { + protocol_(@{name: name, states: dvec()}) +} + +impl methods for protocol { + fn add_state(name: ident, dir: direction) -> state { + self.add_state_poly(name, dir, ~[]) + } + + fn add_state_poly(name: ident, dir: direction, + +ty_params: ~[ast::ty_param]) -> state { + let messages = dvec(); + + let state = state_(@{ + name: name, + dir: dir, + ty_params: ty_params, + messages: messages, + proto: self + }); + + self.states.push(state); + state + } + + fn filename() -> str { + "proto://" + *self.name + } + + fn gen_init(cx: ext_ctxt) -> @ast::item { + let start_state = self.states[0]; + + let body = alt start_state.dir { + send { cx.parse_expr("pipes::entangle()") } + recv { + cx.parse_expr("{ \ + let (s, c) = pipes::entangle(); \ + (c, s) \ + }") + } + }; + + parse_item_from_source_str( + self.filename(), + @#fmt("fn init%s() -> (client::%s, server::%s)\ + { %s }", + start_state.ty_params.to_source(), + start_state.to_ty(cx).to_source(), + start_state.to_ty(cx).to_source(), + body.to_source()), + cx.cfg(), + []/~, + ast::public, + cx.parse_sess()).get() + } + + fn compile(cx: ext_ctxt) -> @ast::item { + let mut items = ~[self.gen_init(cx)]; + let mut client_states = ~[]; + let mut server_states = ~[]; + + for self.states.each |s| { + items += s.to_type_decls(cx); + + client_states += s.to_endpoint_decls(cx, send); + server_states += s.to_endpoint_decls(cx, recv); + } + + vec::push(items, + cx.item_mod(@"client", + client_states)); + vec::push(items, + cx.item_mod(@"server", + server_states)); + + cx.item_mod(self.name, items) + } +} + +iface to_source { + // Takes a thing and generates a string containing rust code for it. + fn to_source() -> str; +} + +impl of to_source for @ast::item { + fn to_source() -> str { + item_to_str(self) + } +} + +impl of to_source for [@ast::item]/~ { + fn to_source() -> str { + str::connect(self.map(|i| i.to_source()), "\n\n") + } +} + +impl of to_source for @ast::ty { + fn to_source() -> str { + ty_to_str(self) + } +} + +impl of to_source for [@ast::ty]/~ { + fn to_source() -> str { + str::connect(self.map(|i| i.to_source()), ", ") + } +} + +impl of to_source for ~[ast::ty_param] { + fn to_source() -> str { + pprust::typarams_to_str(self) + } +} + +impl of to_source for @ast::expr { + fn to_source() -> str { + pprust::expr_to_str(self) + } +} + +impl parse_utils for ext_ctxt { + fn parse_item(s: str) -> @ast::item { + let res = parse::parse_item_from_source_str( + "***protocol expansion***", + @(copy s), + self.cfg(), + []/~, + ast::public, + self.parse_sess()); + alt res { + some(ast) { ast } + none { + #error("Parse error with ```\n%s\n```", s); + fail + } + } + } + + fn parse_expr(s: str) -> @ast::expr { + parse::parse_expr_from_source_str( + "***protocol expansion***", + @(copy s), + self.cfg(), + self.parse_sess()) + } +} + +impl methods for ([A]/~, [B]/~) { + fn zip() -> [(A, B)]/~ { + let (a, b) = self; + vec::zip(a, b) + } + + fn map(f: fn(A, B) -> C) -> [C]/~ { + let (a, b) = self; + vec::map2(a, b, f) + } +} diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc index 5a384f04cb338..852c815e9fe21 100644 --- a/src/libsyntax/syntax.rc +++ b/src/libsyntax/syntax.rc @@ -79,5 +79,8 @@ mod ext { mod auto_serialize; mod source_util; - mod pipes; + mod pipes { + mod pipec; + mod ast_builder; + } } diff --git a/src/test/run-pass/pipe-pingpong-proto.rs b/src/test/run-pass/pipe-pingpong-proto.rs index 0afacb99c1596..479b2059bfe3c 100644 --- a/src/test/run-pass/pipe-pingpong-proto.rs +++ b/src/test/run-pass/pipe-pingpong-proto.rs @@ -1,7 +1,7 @@ -// xfail-test - // An example to make sure the protocol parsing syntax extension works. +// xfail-pretty + proto! pingpong { ping:send { ping -> pong @@ -12,6 +12,42 @@ proto! pingpong { } } +mod test { + import pipes::recv; + import pingpong::{ping, pong}; + + fn client(-chan: pingpong::client::ping) { + import pingpong::client; + + let chan = client::ping(chan); + log(error, "Sent ping"); + let pong(_chan) = option::unwrap(recv(chan)); + log(error, "Received pong"); + } + + fn server(-chan: pingpong::server::ping) { + import pingpong::server; + + let ping(chan) = option::unwrap(recv(chan)); + log(error, "Received ping"); + let _chan = server::pong(chan); + log(error, "Sent pong"); + } +} + fn main() { - // TODO: do something with the protocol -} \ No newline at end of file + let (client_, server_) = pingpong::init(); + let client_ = ~mut some(client_); + let server_ = ~mut some(server_); + + do task::spawn |move client_| { + let mut client__ = none; + *client_ <-> client__; + test::client(option::unwrap(client__)); + }; + do task::spawn |move server_| { + let mut server_ˊ = none; + *server_ <-> server_ˊ; + test::server(option::unwrap(server_ˊ)); + }; +} From d09bcc0131935e2ad403aa965e9be06ddcf01c08 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 5 Jul 2012 16:31:19 -0700 Subject: [PATCH 16/52] Adding token tree nonterminals to earley parser. --- src/libsyntax/ext/pipes/pipec.rs | 2 -- src/libsyntax/ext/tt/earley_parser.rs | 1 + src/libsyntax/parse/token.rs | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/ext/pipes/pipec.rs b/src/libsyntax/ext/pipes/pipec.rs index 0ff4ba92b3663..4ad4eef636b24 100644 --- a/src/libsyntax/ext/pipes/pipec.rs +++ b/src/libsyntax/ext/pipes/pipec.rs @@ -105,8 +105,6 @@ impl methods for message { ast::by_copy)], args_ast); - let args = [#fmt("-pipe: %s", *this.data_name())]/~ + args; - let pat = alt (this.dir, next.dir) { (send, send) { "(c, s)" } (send, recv) { "(s, c)" } diff --git a/src/libsyntax/ext/tt/earley_parser.rs b/src/libsyntax/ext/tt/earley_parser.rs index 26db2842ef772..731d79573e7e3 100644 --- a/src/libsyntax/ext/tt/earley_parser.rs +++ b/src/libsyntax/ext/tt/earley_parser.rs @@ -272,6 +272,7 @@ fn parse_nt(p: parser, name: str) -> whole_nt { + token::to_str(*p.reader.interner(), copy p.token)) } } } "path" { token::w_path(p.parse_path_with_tps(false)) } + "tt" { token::w_tt(p.parse_token_tree()) } _ { p.fatal("Unsupported builtin nonterminal parser: " + name)} } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 1fc8b10f38573..ef1d0f4bc47c1 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -96,6 +96,7 @@ enum whole_nt { w_ty( @ast::ty), w_ident(str_num, bool), w_path(@ast::path), + w_tt(ast::token_tree), } fn binop_to_str(o: binop) -> str { From 84434bc0844545e9c3fb392126f3e80913028b9e Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 5 Jul 2012 16:46:32 -0700 Subject: [PATCH 17/52] Recursively expand items, and keep expansion stack, per Paul's code review comments. --- src/libsyntax/ext/expand.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 63b3c0881e14f..4ac7dc19c0007 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -139,7 +139,7 @@ fn expand_item(exts: hashmap, }; let it = alt it.node { ast::item_mac(*) { - expand_item_mac(exts, cx, it) + expand_item_mac(exts, cx, it, fld) } _ { it } }; @@ -150,7 +150,8 @@ fn expand_item(exts: hashmap, } fn expand_item_mac(exts: hashmap, - cx: ext_ctxt, &&it: @ast::item) -> @ast::item { + cx: ext_ctxt, &&it: @ast::item, + fld: ast_fold) -> @ast::item { alt it.node { item_mac({node: mac_invoc_tt(pth, tt), span}) { let extname = pth.idents[0]; @@ -160,7 +161,13 @@ fn expand_item_mac(exts: hashmap, #fmt("macro undefined: '%s'", *extname)) } some(item_tt(expand)) { - expand.expander(cx, it.span, it.ident, tt) + cx.bt_push(expanded_from({call_site: it.span, + callie: {name: *extname, + span: expand.span}})); + let it = fld.fold_item( + expand.expander(cx, it.span, it.ident, tt)); + cx.bt_pop(); + ret it } _ { cx.span_fatal(it.span, #fmt("%s is not a legal here", *extname)) } From 6806aa0e66028a218de96edd72b11e0ffa4de4e2 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 5 Jul 2012 18:01:11 -0700 Subject: [PATCH 18/52] pingpong protocol parses, although I should probably rewrite this to use Paul's Early parser stuff. --- src/libcore/iter-trait.rs | 7 +++++++ src/libsyntax/ext/pipes.rs | 22 ++++++++++++++++------ src/libsyntax/ext/pipes/pipec.rs | 21 +++++++++++++-------- src/libsyntax/syntax.rc | 3 ++- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/libcore/iter-trait.rs b/src/libcore/iter-trait.rs index 1fae52b4afe05..02c32a1bf0db8 100644 --- a/src/libcore/iter-trait.rs +++ b/src/libcore/iter-trait.rs @@ -35,4 +35,11 @@ impl extensions for IMPL_T { fn min() -> A { iter::min(self) } fn max() -> A { iter::max(self) } + + fn find(p: fn(A) -> bool) -> option { + for self.each |i| { + if p(i) { ret some(i) } + } + ret none; + } } diff --git a/src/libsyntax/ext/pipes.rs b/src/libsyntax/ext/pipes.rs index 5a84d764b9f1f..ce9b0b0a2cfac 100644 --- a/src/libsyntax/ext/pipes.rs +++ b/src/libsyntax/ext/pipes.rs @@ -1,17 +1,27 @@ import codemap::span; import ext::base::ext_ctxt; +import ast::tt_delim; +import parse::lexer::{new_tt_reader, reader, tt_reader_as_reader}; +import parse::parser::{parser, SOURCE_FILE}; +import parse::common::parser_common; + +import pipes::parse_proto::proto_parser; import pipes::pipec::*; -fn expand_proto(cx: ext_ctxt, span: span, id: ast::ident, tt: ast::token_tree) +fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident, tt: ast::token_tree) -> @ast::item { - let proto = protocol(id); - let ping = proto.add_state(@"ping", send); - let pong = proto.add_state(@"pong", recv); + let sess = cx.parse_sess(); + let cfg = cx.cfg(); + let body_core = alt tt { tt_delim(tts) { tts } _ {fail}}; + let tt_rdr = new_tt_reader(cx.parse_sess().span_diagnostic, + cx.parse_sess().interner, body_core); + let rdr = tt_rdr as reader; + let rust_parser = parser(sess, cfg, rdr.dup(), SOURCE_FILE); + + let proto = rust_parser.parse_proto(id); - ping.add_message(@"ping", []/~, pong, ~[]); - pong.add_message(@"pong", []/~, ping, ~[]); proto.compile(cx) } \ No newline at end of file diff --git a/src/libsyntax/ext/pipes/pipec.rs b/src/libsyntax/ext/pipes/pipec.rs index 4ad4eef636b24..e9dfffcd543fa 100644 --- a/src/libsyntax/ext/pipes/pipec.rs +++ b/src/libsyntax/ext/pipes/pipec.rs @@ -12,7 +12,7 @@ import print::pprust; import pprust::{item_to_str, ty_to_str}; import ext::base::{mk_ctxt, ext_ctxt}; import parse; -import parse::{parse_item_from_source_str}; +import parse::*; import ast_builder::ast_builder; import ast_builder::methods; @@ -42,7 +42,7 @@ impl methods for direction { enum message { // name, data, current state, next state, next tys - message(ident, ~[@ast::ty], state, state, ~[@ast::ty]) + message(ident, ~[@ast::ty], state, ident, ~[@ast::ty]) } impl methods for message { @@ -89,11 +89,10 @@ impl methods for message { fn gen_send(cx: ext_ctxt) -> @ast::item { alt self { message(id, tys, this, next, next_tys) { + let next = this.proto.get_state(next); + assert next_tys.len() == next.ty_params.len(); let arg_names = tys.mapi(|i, _ty| @("x_" + i.to_str())); - let args = (arg_names, tys).map(|n, t| - *n + ": " + t.to_source()); - let args_ast = (arg_names, tys).map( |n, t| cx.arg_mode(n, t, ast::by_copy) ); @@ -146,9 +145,8 @@ enum state { } impl methods for state { - fn add_message(name: ident, +data: ~[@ast::ty], next: state, + fn add_message(name: ident, +data: ~[@ast::ty], next: ident, +next_tys: ~[@ast::ty]) { - assert next_tys.len() == next.ty_params.len(); self.messages.push(message(name, data, self, next, next_tys)); } @@ -179,6 +177,7 @@ impl methods for state { let message(_, tys, this, next, next_tys) = m; let name = m.name(); + let next = this.proto.get_state(next); let next_name = next.data_name(); let dir = alt this.dir { @@ -239,6 +238,11 @@ impl methods for protocol { self.add_state_poly(name, dir, ~[]) } + /// Get or create a state. + fn get_state(name: ident) -> state { + self.states.find(|i| i.name == name).get() + } + fn add_state_poly(name: ident, dir: direction, +ty_params: ~[ast::ty_param]) -> state { let messages = dvec(); @@ -291,7 +295,8 @@ impl methods for protocol { let mut client_states = ~[]; let mut server_states = ~[]; - for self.states.each |s| { + // :( + for (copy self.states).each |s| { items += s.to_type_decls(cx); client_states += s.to_endpoint_decls(cx, send); diff --git a/src/libsyntax/syntax.rc b/src/libsyntax/syntax.rc index 852c815e9fe21..7c453cc96e453 100644 --- a/src/libsyntax/syntax.rc +++ b/src/libsyntax/syntax.rc @@ -80,7 +80,8 @@ mod ext { mod source_util; mod pipes { - mod pipec; mod ast_builder; + mod parse_proto; + mod pipec; } } From fa4134611dfc54e117f196644d30948a75b7b9eb Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 5 Jul 2012 23:04:56 -0700 Subject: [PATCH 19/52] Fixing an infinite type, updating code to match new Early parser, remembering to add protocol parser. --- src/libsyntax/ext/pipes.rs | 4 +- src/libsyntax/ext/pipes/parse_proto.rs | 63 ++++++++++++++++++++++++++ src/libsyntax/ext/tt/earley_parser.rs | 2 +- src/libsyntax/parse/token.rs | 6 ++- 4 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/libsyntax/ext/pipes/parse_proto.rs diff --git a/src/libsyntax/ext/pipes.rs b/src/libsyntax/ext/pipes.rs index ce9b0b0a2cfac..4ca47254a943d 100644 --- a/src/libsyntax/ext/pipes.rs +++ b/src/libsyntax/ext/pipes.rs @@ -17,7 +17,9 @@ fn expand_proto(cx: ext_ctxt, _sp: span, id: ast::ident, tt: ast::token_tree) let cfg = cx.cfg(); let body_core = alt tt { tt_delim(tts) { tts } _ {fail}}; let tt_rdr = new_tt_reader(cx.parse_sess().span_diagnostic, - cx.parse_sess().interner, body_core); + cx.parse_sess().interner, + none, + body_core); let rdr = tt_rdr as reader; let rust_parser = parser(sess, cfg, rdr.dup(), SOURCE_FILE); diff --git a/src/libsyntax/ext/pipes/parse_proto.rs b/src/libsyntax/ext/pipes/parse_proto.rs new file mode 100644 index 0000000000000..919960f4c82e8 --- /dev/null +++ b/src/libsyntax/ext/pipes/parse_proto.rs @@ -0,0 +1,63 @@ +// Parsing pipes protocols from token trees. + +import parse::parser; +import ast::ident; +import parse::token; + +import pipec::*; + +impl proto_parser for parser { + fn parse_proto(id: ident) -> protocol { + let proto = protocol(id); + + self.expect(token::LBRACE); + + while self.token != token::RBRACE { + self.parse_state(proto); + } + + ret proto; + } + + fn parse_state(proto: protocol) { + let id = self.parse_ident(); + self.expect(token::COLON); + let dir = alt copy self.token { + token::IDENT(n, _) { + self.get_str(n) + } + _ { fail } + }; + self.bump(); + let dir = alt dir { + @"send" { send } + @"recv" { recv } + _ { fail } + }; + + let state = proto.add_state(id, dir); + // TODO: add typarams too. + + self.expect(token::LBRACE); + + while self.token != token::RBRACE { + let mname = self.parse_ident(); + + // TODO: parse data + + self.expect(token::RARROW); + + let next = self.parse_ident(); + // TODO: parse next types + + state.add_message(mname, ~[], next, ~[]); + + alt copy self.token { + token::COMMA { self.bump() } + token::RBRACE { } + _ { fail } + } + } + self.bump(); + } +} diff --git a/src/libsyntax/ext/tt/earley_parser.rs b/src/libsyntax/ext/tt/earley_parser.rs index 731d79573e7e3..191da586ce5a2 100644 --- a/src/libsyntax/ext/tt/earley_parser.rs +++ b/src/libsyntax/ext/tt/earley_parser.rs @@ -272,7 +272,7 @@ fn parse_nt(p: parser, name: str) -> whole_nt { + token::to_str(*p.reader.interner(), copy p.token)) } } } "path" { token::w_path(p.parse_path_with_tps(false)) } - "tt" { token::w_tt(p.parse_token_tree()) } + "tt" { token::w_tt(@p.parse_token_tree()) } _ { p.fatal("Unsupported builtin nonterminal parser: " + name)} } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index ef1d0f4bc47c1..dbc1e8b34e34d 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -96,7 +96,10 @@ enum whole_nt { w_ty( @ast::ty), w_ident(str_num, bool), w_path(@ast::path), - w_tt(ast::token_tree), + // TODO: this seems to cause infinite recursion in + // type_structually_contains if it's not an @-box. We should at least get + // failure instead. + w_tt(@ast::token_tree), } fn binop_to_str(o: binop) -> str { @@ -190,6 +193,7 @@ fn to_str(in: interner<@str>, t: token) -> str { w_stmt(*) { "statement" } w_pat(*) { "pattern" } w_expr(*) { "expression" } w_ty(*) { "type" } w_ident(*) { "identifier" } w_path(*) { "path" } + w_tt(*) { "tt" } } } } From 7b03832c958826b27ea77df91f2d2ac276bb7411 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Thu, 5 Jul 2012 23:14:27 -0700 Subject: [PATCH 20/52] Updating tests to use pipes. --- src/libcore/future.rs | 9 ++ src/libsyntax/ext/pipes/parse_proto.rs | 58 +++++--- ...ing-contracts.rs => msgsend-ring-pipes.rs} | 39 +----- src/test/run-pass/pipe-manual-1.rs | 110 --------------- src/test/run-pass/pipe-manual-2.rs | 93 ------------- src/test/run-pass/pipe-manual-3.rs | 129 ------------------ src/test/run-pass/pipe-select.rs | 70 ++-------- src/test/run-pass/pipe-sleep.rs | 50 ++----- 8 files changed, 71 insertions(+), 487 deletions(-) rename src/test/bench/{msgsend-ring-contracts.rs => msgsend-ring-pipes.rs} (75%) delete mode 100644 src/test/run-pass/pipe-manual-1.rs delete mode 100644 src/test/run-pass/pipe-manual-2.rs delete mode 100644 src/test/run-pass/pipe-manual-3.rs diff --git a/src/libcore/future.rs b/src/libcore/future.rs index 8bc46de26b297..bf3c30a573b86 100644 --- a/src/libcore/future.rs +++ b/src/libcore/future.rs @@ -138,6 +138,15 @@ fn with(future: future, blk: fn(A) -> B) -> B { } // The pipe protocol, generated by pipec +/* +proto! future_pipe { + waiting:recv { + completed(T) -> terminated + } + + terminated { } +} +*/ mod future_pipe { fn init() -> (client::waiting, server::waiting) { { let (s, c) = pipes::entangle(); (c, s) } diff --git a/src/libsyntax/ext/pipes/parse_proto.rs b/src/libsyntax/ext/pipes/parse_proto.rs index 919960f4c82e8..abf19825fdd27 100644 --- a/src/libsyntax/ext/pipes/parse_proto.rs +++ b/src/libsyntax/ext/pipes/parse_proto.rs @@ -10,11 +10,10 @@ impl proto_parser for parser { fn parse_proto(id: ident) -> protocol { let proto = protocol(id); - self.expect(token::LBRACE); - - while self.token != token::RBRACE { - self.parse_state(proto); - } + self.parse_unspanned_seq(token::LBRACE, + token::RBRACE, + {sep: none, trailing_sep_allowed: false}, + |self| self.parse_state(proto)); ret proto; } @@ -35,29 +34,44 @@ impl proto_parser for parser { _ { fail } }; - let state = proto.add_state(id, dir); - // TODO: add typarams too. + let typarms = if self.token == token::LT { + self.parse_ty_params() + } + else { ~[] }; + + let state = proto.add_state_poly(id, dir, typarms); - self.expect(token::LBRACE); + // parse the messages + self.parse_unspanned_seq( + token::LBRACE, token::RBRACE, + {sep: some(token::COMMA), trailing_sep_allowed: true}, + |self| { + let mname = self.parse_ident(); - while self.token != token::RBRACE { - let mname = self.parse_ident(); + let args = if self.token == token::LPAREN { + self.parse_unspanned_seq(token::LPAREN, + token::RPAREN, + {sep: some(token::COMMA), + trailing_sep_allowed: true}, + |p| p.parse_ty(false)) + } + else { ~[] }; - // TODO: parse data + self.expect(token::RARROW); - self.expect(token::RARROW); + let next = self.parse_ident(); - let next = self.parse_ident(); - // TODO: parse next types + let ntys = if self.token == token::LT { + self.parse_unspanned_seq(token::LT, + token::GT, + {sep: some(token::COMMA), + trailing_sep_allowed: true}, + |p| p.parse_ty(false)) + } + else { ~[] }; - state.add_message(mname, ~[], next, ~[]); + state.add_message(mname, args, next, ntys); - alt copy self.token { - token::COMMA { self.bump() } - token::RBRACE { } - _ { fail } - } - } - self.bump(); + }); } } diff --git a/src/test/bench/msgsend-ring-contracts.rs b/src/test/bench/msgsend-ring-pipes.rs similarity index 75% rename from src/test/bench/msgsend-ring-contracts.rs rename to src/test/bench/msgsend-ring-pipes.rs index 9ad3025d33aed..2eb630d642746 100644 --- a/src/test/bench/msgsend-ring-contracts.rs +++ b/src/test/bench/msgsend-ring-pipes.rs @@ -4,7 +4,7 @@ // that things will look really good once we get that lock out of the // message path. -// This version uses semi-automatically compiled channel contracts. +// This version uses automatically compiled channel contracts. // xfail-pretty @@ -13,40 +13,15 @@ import future::future; use std; import std::time; -import ring::server::recv; - -// This module was generated by the pipe compiler. -mod ring { - fn init() -> (client::num, server::num) { pipes::entangle() } - enum num { num(uint, server::num), } - mod client { - fn num(-pipe: num, x_0: uint) -> num { - let (c, s) = pipes::entangle(); - let message = ring::num(x_0, s); - pipes::send(pipe, message); - c - } - type num = pipes::send_packet; - } - mod server { - impl recv for num { - fn recv() -> extern fn(-num) -> ring::num { - fn recv(-pipe: num) -> ring::num { - option::unwrap(pipes::recv(pipe)) - } - recv - } - } - type num = pipes::recv_packet; +import pipes::recv; + +proto! ring { + num:send { + num(uint) -> num } } fn macros() { - #macro[ - [#recv[chan], - chan.recv()(chan)] - ]; - #macro[ [#move[x], unsafe { let y <- *ptr::addr_of(x); y }] @@ -68,7 +43,7 @@ fn thread_ring(i: uint, num_port2 <-> num_port; num_chan = some(ring::client::num(option::unwrap(num_chan2), i * j)); let port = option::unwrap(num_port2); - alt (#recv(port)) { + alt (option::unwrap(recv(port))) { ring::num(_n, p) { //log(error, _n); num_port = some(#move(p)); diff --git a/src/test/run-pass/pipe-manual-1.rs b/src/test/run-pass/pipe-manual-1.rs deleted file mode 100644 index 7efedfe6418ca..0000000000000 --- a/src/test/run-pass/pipe-manual-1.rs +++ /dev/null @@ -1,110 +0,0 @@ -/* - -The first test case using pipes. The idea is to break this into -several stages for prototyping. Here's the plan: - -1. Write an already-compiled protocol using existing ports and chans. - -2. Take the already-compiled version and add the low-level -synchronization code instead. - -3. Write a syntax extension to compile the protocols. - -At some point, we'll need to add support for select. - -*/ - -mod pingpong { - import newcomm::*; - - type pingpong = ~mut option<(chan<()>, port<()>)>; - - fn init() -> (client::ping, server::ping) { - let cp = port(); - let sp = port(); - let cc = chan(sp); - let sc = chan(cp); - - let client = client::ping(~mut some((cc, cp))); - let server = server::ping(~mut some((sc, sp))); - - (client, server) - } - - mod client { - enum ping = pingpong; - enum pong = pingpong; - - fn do_ping(-c: ping) -> pong { - let mut op = none; - op <-> **c; - let (c, s) <- option::unwrap(op); - c.send(()); - let p <- (c, s); - pong(~mut some(p)) - } - - fn do_pong(-c: pong) -> (ping, ()) { - let mut op = none; - op <-> **c; - let (c, s) <- option::unwrap(op); - let d = s.recv(); - let p <- (c, s); - (ping(~mut some(p)), d) - } - } - - mod server { - enum ping = pingpong; - enum pong = pingpong; - - fn do_ping(-c: ping) -> (pong, ()) { - let mut op = none; - op <-> **c; - let (c, s) <- option::unwrap(op); - let d = s.recv(); - let p <- (c, s); - (pong(~mut some(p)), d) - } - - fn do_pong(-c: pong) -> ping { - let mut op = none; - op <-> **c; - let (c, s) <- option::unwrap(op); - c.send(()); - let p <- (c, s); - ping(~mut some(p)) - } - } -} - -fn client(-chan: pingpong::client::ping) { - let chan = pingpong::client::do_ping(chan); - log(error, "Sent ping"); - let (_chan, _data) = pingpong::client::do_pong(chan); - log(error, "Received pong"); -} - -fn server(-chan: pingpong::server::ping) { - let (chan, _data) = pingpong::server::do_ping(chan); - log(error, "Received ping"); - let _chan = pingpong::server::do_pong(chan); - log(error, "Sent pong"); -} - -fn main() { - let (client_, server_) = pingpong::init(); - let client_ = ~mut some(client_); - let server_ = ~mut some(server_); - - do task::spawn |move client_| { - let mut client__ = none; - *client_ <-> client__; - client(option::unwrap(client__)); - }; - do task::spawn |move server_| { - let mut server_ˊ = none; - *server_ <-> server_ˊ; - server(option::unwrap(server_ˊ)); - }; -} diff --git a/src/test/run-pass/pipe-manual-2.rs b/src/test/run-pass/pipe-manual-2.rs deleted file mode 100644 index 0803bbfe53170..0000000000000 --- a/src/test/run-pass/pipe-manual-2.rs +++ /dev/null @@ -1,93 +0,0 @@ -/* -The first test case using pipes. The idea is to break this into -several stages for prototyping. Here's the plan: - -1. Write an already-compiled protocol using existing ports and chans. - -2. Take the already-compiled version and add the low-level -synchronization code instead. (That's what this file attempts to do) - -3. Write a syntax extension to compile the protocols. - -At some point, we'll need to add support for select. - -*/ - -mod pingpong { - enum ping = *pipes::packet; - enum pong = *pipes::packet; - - fn init() -> (client::ping, server::ping) { - pipes::entangle() - } - - mod client { - type ping = pipes::send_packet; - type pong = pipes::recv_packet; - - fn do_ping(-c: ping) -> pong { - let p = pipes::packet(); - - pipes::send(c, pingpong::ping(p)); - pipes::recv_packet(p) - } - - fn do_pong(-c: pong) -> (ping, ()) { - let packet = pipes::recv(c); - if packet == none { - fail "sender closed the connection" - } - (pipes::send_packet(*option::unwrap(packet)), ()) - } - } - - mod server { - type ping = pipes::recv_packet; - type pong = pipes::send_packet; - - fn do_ping(-c: ping) -> (pong, ()) { - let packet = pipes::recv(c); - if packet == none { - fail "sender closed the connection" - } - (pipes::send_packet(*option::unwrap(packet)), ()) - } - - fn do_pong(-c: pong) -> ping { - let p = pipes::packet(); - pipes::send(c, pingpong::pong(p)); - pipes::recv_packet(p) - } - } -} - -fn client(-chan: pingpong::client::ping) { - let chan = pingpong::client::do_ping(chan); - log(error, "Sent ping"); - let (chan, _data) = pingpong::client::do_pong(chan); - log(error, "Received pong"); -} - -fn server(-chan: pingpong::server::ping) { - let (chan, _data) = pingpong::server::do_ping(chan); - log(error, "Received ping"); - let chan = pingpong::server::do_pong(chan); - log(error, "Sent pong"); -} - -fn main() { - let (client_, server_) = pingpong::init(); - let client_ = ~mut some(client_); - let server_ = ~mut some(server_); - - do task::spawn |move client_| { - let mut client__ = none; - *client_ <-> client__; - client(option::unwrap(client__)); - }; - do task::spawn |move server_| { - let mut server_ˊ = none; - *server_ <-> server_ˊ; - server(option::unwrap(server_ˊ)); - }; -} diff --git a/src/test/run-pass/pipe-manual-3.rs b/src/test/run-pass/pipe-manual-3.rs deleted file mode 100644 index 9f05038aadc6c..0000000000000 --- a/src/test/run-pass/pipe-manual-3.rs +++ /dev/null @@ -1,129 +0,0 @@ -/* -The first test case using pipes. The idea is to break this into -several stages for prototyping. Here's the plan: - -1. Write an already-compiled protocol using existing ports and chans. - -2. Take the already-compiled version and add the low-level -synchronization code instead. - -3. Write a syntax extension to compile the protocols. - -At some point, we'll need to add support for select. - -This file does horrible things to pretend we have self-move. - -*/ - -mod pingpong { - enum ping { ping, } - enum ping_message = *pipes::packet; - enum pong { pong, } - enum pong_message = *pipes::packet; - - fn init() -> (client::ping, server::ping) { - pipes::entangle() - } - - mod client { - type ping = pipes::send_packet; - type pong = pipes::recv_packet; - } - - impl abominable for client::ping { - fn send() -> fn@(-client::ping, ping) -> client::pong { - |pipe, data| { - let p = pipes::packet(); - pipes::send(pipe, pingpong::ping_message(p)); - pipes::recv_packet(p) - } - } - } - - impl abominable for client::pong { - fn recv() -> fn@(-client::pong) -> (client::ping, pong) { - |pipe| { - let packet = pipes::recv(pipe); - if packet == none { - fail "sender closed the connection" - } - let p : pong_message = option::unwrap(packet); - (pipes::send_packet(*p), pong) - } - } - } - - mod server { - type ping = pipes::recv_packet; - type pong = pipes::send_packet; - } - - impl abominable for server::ping { - fn recv() -> fn@(-server::ping) -> (server::pong, ping) { - |pipe| { - let packet = pipes::recv(pipe); - if packet == none { - fail "sender closed the connection" - } - let p : ping_message = option::unwrap(packet); - (pipes::send_packet(*p), ping) - } - } - } - - impl abominable for server::pong { - fn send() -> fn@(-server::pong, pong) -> server::ping { - |pipe, data| { - let p = pipes::packet(); - pipes::send(pipe, pingpong::pong_message(p)); - pipes::recv_packet(p) - } - } - } -} - -mod test { - import pingpong::{ping, pong, abominable}; - - fn macros() { - #macro[ - [#send[chan, data], - chan.send()(chan, data)] - ]; - #macro[ - [#recv[chan], - chan.recv()(chan)] - ]; - } - - fn client(-chan: pingpong::client::ping) { - let chan = #send(chan, ping); - log(error, "Sent ping"); - let (chan, _data) = #recv(chan); - log(error, "Received pong"); - } - - fn server(-chan: pingpong::server::ping) { - let (chan, _data) = #recv(chan); - log(error, "Received ping"); - let chan = #send(chan, pong); - log(error, "Sent pong"); - } -} - -fn main() { - let (client_, server_) = pingpong::init(); - let client_ = ~mut some(client_); - let server_ = ~mut some(server_); - - do task::spawn |move client_| { - let mut client__ = none; - *client_ <-> client__; - test::client(option::unwrap(client__)); - }; - do task::spawn |move server_| { - let mut server_ˊ = none; - *server_ <-> server_ˊ; - test::server(option::unwrap(server_ˊ)); - }; -} diff --git a/src/test/run-pass/pipe-select.rs b/src/test/run-pass/pipe-select.rs index 163cfa954884a..af01e568ffd40 100644 --- a/src/test/run-pass/pipe-select.rs +++ b/src/test/run-pass/pipe-select.rs @@ -1,72 +1,22 @@ +// xfail-pretty + use std; import std::timer::sleep; import std::uv; import pipes::{recv, select}; -// Compiled by pipec -mod oneshot { - fn init() -> (client::waiting, server::waiting) { pipes::entangle() } - enum waiting { signal(server::signaled), } - enum signaled { } - mod client { - fn signal(-pipe: waiting) -> signaled { - let (c, s) = pipes::entangle(); - let message = oneshot::signal(s); - pipes::send(pipe, message); - c - } - type waiting = pipes::send_packet; - type signaled = pipes::send_packet; - } - mod server { - impl recv for waiting { - fn recv() -> extern fn(-waiting) -> oneshot::waiting { - fn recv(-pipe: waiting) -> oneshot::waiting { - option::unwrap(pipes::recv(pipe)) - } - recv - } - } - type waiting = pipes::recv_packet; - impl recv for signaled { - fn recv() -> extern fn(-signaled) -> oneshot::signaled { - fn recv(-pipe: signaled) -> oneshot::signaled { - option::unwrap(pipes::recv(pipe)) - } - recv - } - } - type signaled = pipes::recv_packet; +proto! oneshot { + waiting:send { + signal -> signaled } + + signaled:send { } } -mod stream { - fn init() -> (client::stream, server::stream) { - pipes::entangle() - } - enum stream { send(T, server::stream), } - mod client { - fn send(+pipe: stream, +x_0: T) -> stream { - { - let (c, s) = pipes::entangle(); - let message = stream::send(x_0, s); - pipes::send(pipe, message); - c - } - } - type stream = pipes::send_packet>; - } - mod server { - impl recv for stream { - fn recv() -> extern fn(+stream) -> stream::stream { - fn recv(+pipe: stream) -> stream::stream { - option::unwrap(pipes::recv(pipe)) - } - recv - } - } - type stream = pipes::recv_packet>; +proto! stream { + stream:send { + send(T) -> stream } } diff --git a/src/test/run-pass/pipe-sleep.rs b/src/test/run-pass/pipe-sleep.rs index 932a4f9a43221..c4b44ff0be729 100644 --- a/src/test/run-pass/pipe-sleep.rs +++ b/src/test/run-pass/pipe-sleep.rs @@ -1,54 +1,22 @@ +// xfail-pretty + use std; import std::timer::sleep; import std::uv; +import pipes::recv; -// Compiled by pipec -mod oneshot { - fn init() -> (client::waiting, server::waiting) { pipes::entangle() } - enum waiting { signal(server::signaled), } - enum signaled { } - mod client { - fn signal(-pipe: waiting) -> signaled { - let (c, s) = pipes::entangle(); - let message = oneshot::signal(s); - pipes::send(pipe, message); - c - } - type waiting = pipes::send_packet; - type signaled = pipes::send_packet; - } - mod server { - impl recv for waiting { - fn recv() -> extern fn(-waiting) -> oneshot::waiting { - fn recv(-pipe: waiting) -> oneshot::waiting { - option::unwrap(pipes::recv(pipe)) - } - recv - } - } - type waiting = pipes::recv_packet; - impl recv for signaled { - fn recv() -> extern fn(-signaled) -> oneshot::signaled { - fn recv(-pipe: signaled) -> oneshot::signaled { - option::unwrap(pipes::recv(pipe)) - } - recv - } - } - type signaled = pipes::recv_packet; +proto! oneshot { + waiting:send { + signal -> signaled } + + signaled:send { } } fn main() { import oneshot::client::*; - import oneshot::server::recv; - - #macro[ - [#recv[chan], - chan.recv()(chan)] - ]; - let c = pipes::spawn_service(oneshot::init, |p| { #recv(p); }); + let c = pipes::spawn_service(oneshot::init, |p| { recv(p); }); let iotask = uv::global_loop::get(); sleep(iotask, 5000); From 604f7c66ff4c61ea593e060eb6f8d055c89b64e8 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 6 Jul 2012 11:05:28 -0700 Subject: [PATCH 21/52] Removing locked queue port/chan prototype. --- src/libcore/core.rc | 3 +- src/libcore/newcomm.rs | 85 ------------------------------ src/test/bench/msgsend-ring-new.rs | 77 --------------------------- 3 files changed, 1 insertion(+), 164 deletions(-) delete mode 100644 src/libcore/newcomm.rs delete mode 100644 src/test/bench/msgsend-ring-new.rs diff --git a/src/libcore/core.rc b/src/libcore/core.rc index de0e7fb8f6723..d17cef756295b 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -39,7 +39,7 @@ export float, f32, f64; export box, char, str, ptr, vec, bool; export either, option, result, iter; export libc, os, io, run, rand, sys, unsafe, logging; -export arc, newcomm, comm, task, future, pipes; +export arc, comm, task, future, pipes; export extfmt; export tuple; export to_str, to_bytes; @@ -183,7 +183,6 @@ mod dlist_iter { // Concurrency mod arc; -mod newcomm; mod comm; mod task; mod future; diff --git a/src/libcore/newcomm.rs b/src/libcore/newcomm.rs deleted file mode 100644 index ec3c37d151d65..0000000000000 --- a/src/libcore/newcomm.rs +++ /dev/null @@ -1,85 +0,0 @@ -/** - * A new implementation of communication. - * - * This should be implementing almost entirely in Rust, and hopefully - * avoid needing a single global lock. - */ - -import arc::methods; -import dvec::dvec; -import dvec::extensions; -import sys::methods; - -export port; -export chan; -export send, recv; -export methods; - -type raw_port = arc::exclusive>; - -enum port { - port_(raw_port) -} -enum chan { - chan_(raw_port) -} - -fn port() -> port { - port_(arc::exclusive(dvec())) -} - -fn chan(p: port) -> chan { - chan_((*p).clone()) -} - -fn send(c: chan, -x: T) { - let mut x <- some(x); - do (*c).with |cond, data| { - let mut xx = none; - xx <-> x; - (*data).push(option::unwrap(xx)); - cond.signal(); - } -} - -fn recv(p: port) -> T { - do (*p).with |cond, data| { - if (*data).len() == 0u { - cond.wait(); - } - assert (*data).len() > 0u; - (*data).shift() - } -} - -impl methods for chan { - fn send(-x: T) { - send(self, x) - } - - fn clone() -> chan { - chan_((*self).clone()) - } -} - -impl methods for port { - fn recv() -> T { - recv(self) - } - - fn chan() -> chan { - chan(self) - } -} - -#[cfg(test)] -mod test { - #[test] - fn newport_simple() { - let p = port(); - let c = chan(p); - - c.send(42); - assert p.recv() == 42; - } -} diff --git a/src/test/bench/msgsend-ring-new.rs b/src/test/bench/msgsend-ring-new.rs deleted file mode 100644 index 93f78df7d7e27..0000000000000 --- a/src/test/bench/msgsend-ring-new.rs +++ /dev/null @@ -1,77 +0,0 @@ -// This test creates a bunch of tasks that simultaneously send to each -// other in a ring. The messages should all be basically -// independent. It's designed to hammer the global kernel lock, so -// that things will look really good once we get that lock out of the -// message path. - -import newcomm::*; -import future::future; -import future::extensions; - -use std; -import std::time; - -fn thread_ring(i: uint, - count: uint, - num_chan: chan, - num_port: port) { - // Send/Receive lots of messages. - for uint::range(0u, count) |j| { - num_chan.send(i * j); - num_port.recv(); - }; -} - -fn main(args: ~[str]) { - let args = if os::getenv("RUST_BENCH").is_some() { - ~["", "100", "10000"] - } else if args.len() <= 1u { - ~["", "100", "1000"] - } else { - args - }; - - let num_tasks = option::get(uint::from_str(args[1])); - let msg_per_task = option::get(uint::from_str(args[2])); - - let num_port = port(); - let mut num_chan = chan(num_port); - - let start = time::precise_time_s(); - - // create the ring - let mut futures = ~[]; - - for uint::range(1u, num_tasks) |i| { - let get_chan = port(); - let get_chan_chan = chan(get_chan); - { - let num_chan = num_chan.clone(); - futures += ~[do future::spawn |move num_chan, move get_chan_chan| { - let p = port(); - get_chan_chan.send(chan(p)); - thread_ring(i, msg_per_task, num_chan, p) - }]; - } - - num_chan = get_chan.recv(); - }; - - // do our iteration - thread_ring(0u, msg_per_task, num_chan, num_port); - - // synchronize - for futures.each |f| { f.get() }; - - let stop = time::precise_time_s(); - - // all done, report stats. - let num_msgs = num_tasks * msg_per_task; - let elapsed = (stop - start); - let rate = (num_msgs as float) / elapsed; - - io::println(#fmt("Sent %? messages in %? seconds", - num_msgs, elapsed)); - io::println(#fmt(" %? messages / second", rate)); - io::println(#fmt(" %? μs / message", 1000000. / rate)); -} From 08a99d6ae11431fd7fab25037e3a48f4e420f0dc Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 12:14:32 -0700 Subject: [PATCH 22/52] tutorial: Add a language overview to the introduction --- doc/tutorial.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/tutorial.md b/doc/tutorial.md index a6d2f153f0c40..9d85f9b3c6654 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -11,6 +11,33 @@ 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 + +Rust is a systems programming language with a focus on type safety, +memory safety, and performance. It is intended for writing large, high +performance applications while preventing several classes of errors +commonly found in languages like C++. Rust has a sophisticated memory +model that enables many of the efficient data structures used in C +while disallowing invalid memory access that would otherwise cause +segmentation faults. Like other systems languages it is statically +typed and compiled ahead of time. + +As a multi-paradigm language it has strong support for writing code in +procedural, functional and object-oriented styles. Some of it's nice +high-level features include: + +* Pattern matching and algebraic data types (enums) - common in functional + languages, pattern matching on ADTs provides a compact and expressive + way to encode program logic +* Task-based concurrency - Rust uses lightweight tasks that do not share + memory +* Higher-order functions - Closures in Rust are very powerful and used + pervasively +* Polymorphism - Rust's type system features a unique combination of + Java-style interfaces and Haskell-style typeclasses +* Generics - Functions and types can be parameterized over generic + types with optional type constraints + ## First Impressions As a curly-brace language in the tradition of C, C++, and JavaScript, From 8c64a98d657b27a6b324547eca78c032d191e33e Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 12:20:03 -0700 Subject: [PATCH 23/52] tutorial: Minor tweaks to intro --- doc/tutorial.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 9d85f9b3c6654..764ce2748a4ff 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -14,13 +14,13 @@ the whole language, though not with the depth and precision of the ## Language Overview Rust is a systems programming language with a focus on type safety, -memory safety, and performance. It is intended for writing large, high -performance applications while preventing several classes of errors -commonly found in languages like C++. Rust has a sophisticated memory -model that enables many of the efficient data structures used in C -while disallowing invalid memory access that would otherwise cause -segmentation faults. Like other systems languages it is statically -typed and compiled ahead of time. +memory safety, concurrency and performance. It is intended for writing +large, high performance applications while preventing several classes +of errors commonly found in languages like C++. Rust has a +sophisticated memory model that enables many of the efficient data +structures used in C++ while disallowing invalid memory access that +would otherwise cause segmentation faults. Like other systems +languages it is statically typed and compiled ahead of time. As a multi-paradigm language it has strong support for writing code in procedural, functional and object-oriented styles. Some of it's nice From ee0177b9082ffc2521141c615d332a994708c8a2 Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Fri, 6 Jul 2012 11:35:46 -0700 Subject: [PATCH 24/52] Move string append to libraries. Closes #2710. --- src/libcore/char.rs | 6 +-- src/libcore/rand.rs | 6 +-- src/libcore/str.rs | 58 ++++++++++++++++++----- src/libcore/to_str.rs | 6 +-- src/rustc/middle/ty.rs | 4 +- src/test/run-pass/module-polymorphism.rc | 2 +- src/test/run-pass/module-polymorphism4.rc | 2 - 7 files changed, 59 insertions(+), 25 deletions(-) diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 28645df1b46c0..d68800a96fc78 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -139,9 +139,9 @@ fn escape_unicode(c: char) -> str { else { ('U', 8u) }); assert str::len(s) <= pad; let mut out = "\\"; - out += str::from_char(c); - for uint::range(str::len(s), pad) |_i| { out += "0"; } - out += s; + str::push_str(out, str::from_char(c)); + for uint::range(str::len(s), pad) |_i| { str::push_str(out, "0"); } + str::push_str(out, s); ret out; } diff --git a/src/libcore/rand.rs b/src/libcore/rand.rs index 4db2cdb086d5e..ce4a29f376ae5 100644 --- a/src/libcore/rand.rs +++ b/src/libcore/rand.rs @@ -144,9 +144,9 @@ impl extensions for rng { * Return a random string of the specified length composed of A-Z,a-z,0-9 */ fn gen_str(len: uint) -> str { - let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "abcdefghijklmnopqrstuvwxyz" + - "0123456789"; + let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + abcdefghijklmnopqrstuvwxyz\ + 0123456789"; let mut s = ""; let mut i = 0u; while (i < len) { diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 43a9e8f6981bf..2d9d97979097e 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -15,6 +15,7 @@ export from_byte, from_char, from_chars, + append, concat, connect, @@ -25,6 +26,7 @@ export unpack_slice, // Adding things to and removing things from a string + push_str, push_char, pop_char, shift_char, @@ -233,10 +235,38 @@ pure fn from_chars(chs: &[const char]) -> str { ret buf; } +/// Appends a string slice to the back of a string +#[inline(always)] +fn push_str(&lhs: str, rhs: str/&) { + unsafe { + let llen = lhs.len(); + let rlen = rhs.len(); + reserve(lhs, llen + rlen); + do as_buf(lhs) |lbuf| { + do unpack_slice(rhs) |rbuf, _rlen| { + let dst = ptr::offset(lbuf, llen); + ptr::memcpy(dst, rbuf, rlen); + } + } + unsafe::set_len(lhs, llen + rlen); + } +} + +/// Concatenate two strings together +#[inline(always)] +pure fn append(+lhs: str, rhs: str/&) -> str { + let mut v <- lhs; + unchecked { + push_str(v, rhs); + } + ret v; +} + + /// Concatenate a vector of strings pure fn concat(v: &[const str]) -> str { let mut s: str = ""; - for vec::each(v) |ss| { s += ss; } + for vec::each(v) |ss| { unchecked { push_str(s, ss) }; } ret s; } @@ -244,8 +274,8 @@ pure fn concat(v: &[const str]) -> str { pure fn connect(v: &[const str], sep: str) -> str { let mut s = "", first = true; for vec::each(v) |ss| { - if first { first = false; } else { s += sep; } - s += ss; + if first { first = false; } else { unchecked { push_str(s, sep); } } + unchecked { push_str(s, ss) }; } ret s; } @@ -576,8 +606,8 @@ pure fn to_upper(s: str/&) -> str { pure fn replace(s: str, from: str, to: str) -> str { let mut result = "", first = true; do iter_between_matches(s, from) |start, end| { - if first { first = false; } else { result += to; } - unsafe { result += unsafe::slice_bytes(s, start, end); } + if first { first = false; } else { unchecked {push_str(result, to); }} + unsafe { push_str(result, unsafe::slice_bytes(s, start, end)); } } result } @@ -1694,7 +1724,7 @@ pure fn escape_default(s: str/&) -> str { let mut out: str = ""; unchecked { reserve_at_least(out, str::len(s)); - chars_iter(s, |c| out += char::escape_default(c)); + chars_iter(s, |c| push_str(out, char::escape_default(c))); } ret out; } @@ -1704,7 +1734,7 @@ pure fn escape_unicode(s: str/&) -> str { let mut out: str = ""; unchecked { reserve_at_least(out, str::len(s)); - chars_iter(s, |c| out += char::escape_unicode(c)); + chars_iter(s, |c| push_str(out, char::escape_unicode(c))); } ret out; } @@ -1863,6 +1893,12 @@ impl extensions for str { /// Returns a string with trailing whitespace removed #[inline] fn trim_right() -> str { trim_right(self) } + + /// Concatenate two strings: operator version + #[inline(always)] + pure fn +(rhs: str/&) -> str { + append(self, rhs) + } } /// Extension methods for strings @@ -2311,13 +2347,13 @@ mod tests { fn a_million_letter_a() -> str { let mut i = 0; let mut rs = ""; - while i < 100000 { rs += "aaaaaaaaaa"; i += 1; } + while i < 100000 { push_str(rs, "aaaaaaaaaa"); i += 1; } ret rs; } fn half_a_million_letter_a() -> str { let mut i = 0; let mut rs = ""; - while i < 100000 { rs += "aaaaa"; i += 1; } + while i < 100000 { push_str(rs, "aaaaa"); i += 1; } ret rs; } assert eq(half_a_million_letter_a(), @@ -2422,13 +2458,13 @@ mod tests { fn a_million_letter_X() -> str { let mut i = 0; let mut rs = ""; - while i < 100000 { rs += "华华华华华华华华华华"; i += 1; } + while i < 100000 { push_str(rs, "华华华华华华华华华华"); i += 1; } ret rs; } fn half_a_million_letter_X() -> str { let mut i = 0; let mut rs = ""; - while i < 100000 { rs += "华华华华华"; i += 1; } + while i < 100000 { push_str(rs, "华华华华华"); i += 1; } ret rs; } assert eq(half_a_million_letter_X(), diff --git a/src/libcore/to_str.rs b/src/libcore/to_str.rs index 359f5ea4a9cd8..c54a8887f8465 100644 --- a/src/libcore/to_str.rs +++ b/src/libcore/to_str.rs @@ -61,10 +61,10 @@ impl of to_str for ~[A] { let mut acc = "[", first = true; for vec::each(self) |elt| { if first { first = false; } - else { acc += ", "; } - acc += elt.to_str(); + else { str::push_str(acc, ", "); } + str::push_str(acc, elt.to_str()); } - acc += "]"; + str::push_char(acc, ']'); acc } } diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 6580feab3301c..fba8d54cfba01 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -2963,8 +2963,8 @@ fn is_binopable(_cx: ctxt, ty: t, op: ast::binop) -> bool { /*bool*/ ~[f, f, f, f, t, t, t, t], /*int*/ ~[t, t, t, t, t, t, t, f], /*float*/ ~[t, t, t, f, t, t, f, f], - /*str*/ ~[t, f, f, f, t, t, f, f], - /*vec*/ ~[t, f, f, f, t, t, f, f], + /*str*/ ~[f, f, f, f, t, t, f, f], + /*vec*/ ~[f, f, f, f, t, t, f, f], /*bot*/ ~[f, f, f, f, t, t, f, f], /*struct*/ ~[t, t, t, t, t, t, t, t]]; diff --git a/src/test/run-pass/module-polymorphism.rc b/src/test/run-pass/module-polymorphism.rc index ed8e8be283ea6..781e3a279b194 100644 --- a/src/test/run-pass/module-polymorphism.rc +++ b/src/test/run-pass/module-polymorphism.rc @@ -42,4 +42,4 @@ mod f32 { #[path = "template.rs"] mod template; -} \ No newline at end of file +} diff --git a/src/test/run-pass/module-polymorphism4.rc b/src/test/run-pass/module-polymorphism4.rc index 04348ad6dc220..bbca74cd397cc 100644 --- a/src/test/run-pass/module-polymorphism4.rc +++ b/src/test/run-pass/module-polymorphism4.rc @@ -1,5 +1,3 @@ -#[no_core]; - #[path = "module-polymorphism4-files"] mod cat { From 11d868e92538e34c84593df0b891a4185c4520be Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 6 Jul 2012 13:59:17 -0700 Subject: [PATCH 25/52] paper over #2586 by not failing when the key is not found --- src/rustc/util/ppaux.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index c8238d6b05704..86a2eb65b90bb 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -52,6 +52,10 @@ fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> str { ast_map::node_id_to_str(cx.items, node_id)]) } } } + none { + // FIXME(#2586) + #fmt["", node_id] + } _ { cx.sess.bug( #fmt["re_scope refers to %s", ast_map::node_id_to_str(cx.items, node_id)]) } From 0e1a6cf3d902065327625ff05ebbd7836bdb001b Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 6 Jul 2012 14:13:42 -0700 Subject: [PATCH 26/52] Remove tvec::trans_add --- src/rustc/middle/trans/base.rs | 3 --- src/rustc/middle/trans/tvec.rs | 42 ---------------------------------- 2 files changed, 45 deletions(-) diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index cff52a8c1ddd1..83a9847c163b7 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1660,9 +1660,6 @@ fn trans_eager_binop(cx: block, span: span, op: ast::binop, lhs: ValueRef, let rhs = cast_shift_expr_rhs(cx, op, lhs, rhs); - if op == ast::add && ty::type_is_sequence(intype) { - ret tvec::trans_add(cx, intype, lhs, rhs, dest); - } let mut cx = cx; let val = alt op { ast::add { diff --git a/src/rustc/middle/trans/tvec.rs b/src/rustc/middle/trans/tvec.rs index a15bb2938139b..d3212cd99e060 100644 --- a/src/rustc/middle/trans/tvec.rs +++ b/src/rustc/middle/trans/tvec.rs @@ -375,48 +375,6 @@ fn trans_append_literal(bcx: block, vptrptr: ValueRef, vec_ty: ty::t, bcx } -fn trans_add(bcx: block, vec_ty: ty::t, lhs: ValueRef, - rhs: ValueRef, dest: dest) -> block { - let _icx = bcx.insn_ctxt("tvec::trans_add"); - let ccx = bcx.ccx(); - - let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); - let llunitty = type_of::type_of(ccx, unit_ty); - - if ty::get(vec_ty).struct == ty::ty_str { - let lhs = PointerCast(bcx, lhs, T_ptr(T_i8())); - let rhs = PointerCast(bcx, rhs, T_ptr(T_i8())); - let n = Call(bcx, ccx.upcalls.str_concat, ~[lhs, rhs]); - let n = PointerCast( - bcx, n, T_unique_ptr(T_unique(ccx, T_vec(ccx, llunitty)))); - ret base::store_in_dest(bcx, n, dest); - } - - let lhs_fill = get_fill(bcx, get_bodyptr(bcx, lhs)); - let rhs_fill = get_fill(bcx, get_bodyptr(bcx, rhs)); - let new_fill = Add(bcx, lhs_fill, rhs_fill); - let mut {bcx: bcx, val: new_vec_ptr} = - alloc_uniq_raw(bcx, unit_ty, new_fill, new_fill); - - let new_vec_body_ptr = get_bodyptr(bcx, new_vec_ptr); - let write_ptr_ptr = do_spill_noroot - (bcx, get_dataptr(bcx, new_vec_body_ptr)); - let copy_fn = fn@(bcx: block, addr: ValueRef, - _ty: ty::t) -> block { - let ccx = bcx.ccx(); - 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); - ret bcx; - }; - - let bcx = iter_vec_uniq(bcx, lhs, vec_ty, lhs_fill, copy_fn); - let bcx = iter_vec_uniq(bcx, rhs, vec_ty, rhs_fill, copy_fn); - ret base::store_in_dest(bcx, new_vec_ptr, dest); -} - type val_and_ty_fn = fn@(block, ValueRef, ty::t) -> result; type iter_vec_block = fn(block, ValueRef, ty::t) -> block; From 82001412f2a454158d8e29d7d1a4d1122e734e57 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 14:34:41 -0700 Subject: [PATCH 27/52] tutorial: Add a section on the memory model --- doc/tutorial.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/doc/tutorial.md b/doc/tutorial.md index 764ce2748a4ff..7872a695827f9 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -879,6 +879,82 @@ while (x > 10) { x -= 10; } assert x == 10; ~~~~ +# The Rust Memory Model + +At this junction let's take a detour to explain the concepts involved +in Rust's memory model. Rust has a very particular approach to +memory management that plays a significant role in shaping the "feel" +of the language. Understanding the memory landscape will illuminate +several of Rust's unique features as we encounter them. + +Rust has three competing goals that inform its view of memory: + +* Memory safety - memory that is managed by and is accessible to + the Rust language must be guaranteed to be valid. Under normal + circumstances it is impossible for Rust to trigger a segmentation + fault or leak 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 + for code running in parallel + +## How performance considerations influence the memory model + +Many languages that ofter the kinds of memory safety guarentees that +Rust does have a single allocation strategy: objects live on the heap, +live for as long as they are needed, and are periodically garbage +collected. This is very straightforword both conceptually and in +implementation, but has very significant costs. Such languages tend to +aggressively pursue ways to ameliorate allocation costs (think the +Java virtual machine). Rust supports this strategy with _shared +boxes_, memory allocated on the heap that may be referred to (shared) +by multiple variables. + +In comparison, languages like C++ offer a very precise control over +where objects are allocated. In particular, it is common to put +them directly on the stack, avoiding expensive heap allocation. In +Rust this is possible as well, and the compiler will use a clever +lifetime analysis to ensure that no variable can refer to stack +objects after they are destroyed. + +## How concurrency considerations influence the memory model + +Memory safety in a concurrent environment tends to mean avoiding race +conditions between two threads of execution accessing the same +memory. Even high-level languages frequently avoid solving this +problem, requiring programmers to correctly employ locking to unsure +their program is free of races. + +Rust solves this problem by starting from the position that memory +simply cannot be shared between tasks. Experience in other languages +has proven that isolating each tasks' heap from each other is +a reliable strategy and one that is easy for programmers to reason +about. Having isolated heaps additionally means that garbage collection +must only be done per-heap. Rust never 'stops the world' to garbage +collect memory. + +If Rust tasks have completely isolated heaps then that seems to imply +that any data transferred between them must be copied. While this +is a fine and useful way to implement communication between tasks, +it is also very inefficient for large data structures. + +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. + +## What to be aware of + +Rust has three "realms" in which objects can be allocated: the stack, +the local heap, and the exchange heap. These realms have corresponding +pointer types: the borrowed pointer (`&T`), the shared pointer (`@T`), +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 From ea4cd49e9f81389bb4f709df17810b0dff3a918a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jul 2012 14:52:21 -0700 Subject: [PATCH 28/52] tutorial: Minor tweak --- doc/tutorial.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index 7872a695827f9..b662a218eb48f 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -926,13 +926,12 @@ memory. Even high-level languages frequently avoid solving this problem, requiring programmers to correctly employ locking to unsure their program is free of races. -Rust solves this problem by starting from the position that memory -simply cannot be shared between tasks. Experience in other languages -has proven that isolating each tasks' heap from each other is -a reliable strategy and one that is easy for programmers to reason -about. Having isolated heaps additionally means that garbage collection -must only be done per-heap. Rust never 'stops the world' to garbage -collect memory. +Rust starts from the position that memory simply cannot be shared +between tasks. Experience in other languages has proven that isolating +each tasks' heap from each other is a reliable strategy and one that +is easy for programmers to reason about. Having isolated heaps +additionally means that garbage collection must only be done +per-heap. Rust never 'stops the world' to garbage collect memory. If Rust tasks have completely isolated heaps then that seems to imply that any data transferred between them must be copied. While this From 702f0cd73456cc72e030c1c936f83ec2523d9b0f Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Fri, 6 Jul 2012 14:50:45 -0700 Subject: [PATCH 29/52] Rename dvec::from_elt to dvec::from_elem. Closes #2792. --- src/libcore/dvec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/dvec.rs b/src/libcore/dvec.rs index 9c1d6cae36b14..ec396f474a7cf 100644 --- a/src/libcore/dvec.rs +++ b/src/libcore/dvec.rs @@ -10,7 +10,7 @@ import unsafe::reinterpret_cast; import ptr::{null, extensions}; export dvec; -export from_elt; +export from_elem; export from_vec; export extensions; export unwrap; @@ -56,7 +56,7 @@ fn dvec() -> dvec { } /// Creates a new dvec with a single element -fn from_elt(+e: A) -> dvec { +fn from_elem(+e: A) -> dvec { {mut data: ~[mut e]} } From 9e6b43fb33c47f99385d6a4c5442eac1f74d273c Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Fri, 6 Jul 2012 15:04:57 -0700 Subject: [PATCH 30/52] 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 31/52] 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 32/52] 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 33/52] 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 34/52] 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 35/52] 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 36/52] 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 37/52] 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 38/52] 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 39/52] 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 40/52] 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 41/52] 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 42/52] 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 43/52] 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 44/52] 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 45/52] 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 46/52] 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 47/52] 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 48/52] 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 49/52] 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 50/52] 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 12f6e96a376e0e41155fa8520dd3809dd5bb8e08 Mon Sep 17 00:00:00 2001 From: Kevin Cantu Date: Fri, 6 Jul 2012 18:20:36 -0700 Subject: [PATCH 51/52] Add the Alioth chameneos-redux benchmark This adds a Rust implementation of the Alioth chameneos-redux benchmark: http://shootout.alioth.debian.org/u64q/performance.php?test=chameneosredux This version already seems faster than Clojure, Ruby, and OCaml. I'm running with N=6,000,000 in about 1m 50s. Further optimization would be good, though. I'm talking right now with @eholk about how pipes could be used (this is 1:many)... --- src/test/bench/shootout-chameneos-redux.rs | 214 +++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 src/test/bench/shootout-chameneos-redux.rs diff --git a/src/test/bench/shootout-chameneos-redux.rs b/src/test/bench/shootout-chameneos-redux.rs new file mode 100644 index 0000000000000..631c612d081f2 --- /dev/null +++ b/src/test/bench/shootout-chameneos-redux.rs @@ -0,0 +1,214 @@ +// chameneos + +import io::reader_util; + +use std; +import std::map; +import std::map::hashmap; +import std::sort; + +fn print_complements() { + let all = ~[Blue, Red, Yellow]; + for vec::each(all) |aa| { + for vec::each(all) |bb| { + io::println(show_color(aa) + " + " + show_color(bb) + + " -> " + show_color(transform(aa,bb))); + } + } +} + +// can I combine these two lines? +enum color_e { Red, Yellow, Blue } +type color = color_e; + +type creature_info = { name: uint, color: color }; + +fn show_color(cc: color) -> str { + alt (cc) { + Red {"red"} + Yellow {"yellow"} + Blue {"blue"} + } +} + +fn show_color_list(set: ~[color]) -> str { + let mut out = ""; + for vec::eachi(set) |_ii, col| { + out += " "; + out += show_color(col); + } + ret out; +} + +fn show_digit(nn: uint) -> str { + alt (nn) { + 0 {"zero"} + 1 {"one"} + 2 {"two"} + 3 {"three"} + 4 {"four"} + 5 {"five"} + 6 {"six"} + 7 {"seven"} + 8 {"eight"} + 9 {"nine"} + _ {fail "expected digits from 0 to 9..."} + } +} + +fn show_number(nn: uint) -> str { + let mut out = ""; + let mut num = nn; + let mut dig; + + if num == 0 { out = show_digit(0) }; + + while num != 0 { + dig = num % 10; + num = num / 10; + out = show_digit(dig) + " " + out; + } + + ret out; +} + +fn transform(aa: color, bb: color) -> color { + alt (aa, bb) { + (Red, Red ) { Red } + (Red, Yellow) { Blue } + (Red, Blue ) { Yellow } + (Yellow, Red ) { Blue } + (Yellow, Yellow) { Yellow } + (Yellow, Blue ) { Red } + (Blue, Red ) { Yellow } + (Blue, Yellow) { Red } + (Blue, Blue ) { Blue } + } +} + +fn creature( + name: uint, + color: color, + from_rendezvous: comm::port>, + to_rendezvous: comm::chan, + to_rendezvous_log: comm::chan +) { + let mut color = color; + let mut creatures_met = 0; + let mut evil_clones_met = 0; + + loop { + // ask for a pairing + comm::send(to_rendezvous, {name: name, color: color}); + let resp = comm::recv(from_rendezvous); + + // log and change, or print and quit + alt resp { + option::some(other_creature) { + color = transform(color, other_creature.color); + + // track some statistics + creatures_met += 1; + if other_creature.name == name { + evil_clones_met += 1; + } + } + option::none { + // log creatures met and evil clones of self + let report = #fmt("%u", creatures_met) + " " + + show_number(evil_clones_met); + comm::send(to_rendezvous_log, report); + break; + } + } + } +} + +fn rendezvous(nn: uint, set: ~[color]) { + let from_creatures: comm::port = comm::port(); + let from_creatures_log: comm::port = comm::port(); + let to_rendezvous = comm::chan(from_creatures); + let to_rendezvous_log = comm::chan(from_creatures_log); + let to_creature: ~[comm::chan>] = + vec::mapi(set, + fn@(ii: uint, col: color) -> comm::chan> { + ret do task::spawn_listener |from_rendezvous| { + creature(ii, col, from_rendezvous, to_rendezvous, + to_rendezvous_log); + }; + } + ); + + let mut meetings = 0; + let mut creatures_met = 0; + let mut creatures_present = 0; + + // use option type instead of initializing to junk? + let mut first_creature = { name: 0, color: Red }; + let mut second_creature = { name: 0, color: Red }; + + // set up meetings... + while meetings < nn { + let creature_req: creature_info = comm::recv(from_creatures); + creatures_met += 1; + + alt creatures_present { + 0 { + first_creature = creature_req; + creatures_present = 1; + } + 1 { + second_creature = creature_req; + comm::send(to_creature[first_creature.name], + some(second_creature)); + comm::send(to_creature[second_creature.name], + some(first_creature)); + creatures_present = 0; + meetings += 1; + } + _ { fail "too many creatures are here!" } + } + } + + // tell each creature to stop + for vec::eachi(to_creature) |_ii, to_one| { + comm::send(to_one, none); + } + + // save each creature's meeting stats + let mut report = ~[]; + for vec::each(to_creature) |_to_one| { + vec::push(report, comm::recv(from_creatures_log)); + } + + // print each color in the set + io::println(show_color_list(set)); + + // print each creature's stats + for vec::each(report) |rep| { + io::println(rep); + } + + // print the total number of creatures met + io::println(show_number(creatures_met)); +} + +fn main(args: ~[str]) { + let args = if os::getenv("RUST_BENCH").is_some() || args.len() <= 1u { + ~["", "600"] + } else { + args + }; + + let nn = uint::from_str(args[1]).get(); + + print_complements(); + io::println(""); + + rendezvous(nn, ~[Blue, Red, Yellow]); + io::println(""); + + rendezvous(nn, + ~[Blue, Red, Yellow, Red, Yellow, Blue, Red, Yellow, Red, Blue]); +} + From ec54155b19249b0621ef8092b83d9a5f14fa4090 Mon Sep 17 00:00:00 2001 From: Kevin Cantu Date: Sat, 7 Jul 2012 21:43:12 -0700 Subject: [PATCH 52/52] Add improvements suggested by erickt and bblum --- src/test/bench/shootout-chameneos-redux.rs | 45 ++++++++-------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/src/test/bench/shootout-chameneos-redux.rs b/src/test/bench/shootout-chameneos-redux.rs index 631c612d081f2..9b44f42ee3729 100644 --- a/src/test/bench/shootout-chameneos-redux.rs +++ b/src/test/bench/shootout-chameneos-redux.rs @@ -17,9 +17,7 @@ fn print_complements() { } } -// can I combine these two lines? -enum color_e { Red, Yellow, Blue } -type color = color_e; +enum color { Red, Yellow, Blue } type creature_info = { name: uint, color: color }; @@ -125,13 +123,20 @@ fn creature( } fn rendezvous(nn: uint, set: ~[color]) { + // these ports will allow us to hear from the creatures let from_creatures: comm::port = comm::port(); let from_creatures_log: comm::port = comm::port(); + + // these channels will be passed to the creatures so they can talk to us let to_rendezvous = comm::chan(from_creatures); let to_rendezvous_log = comm::chan(from_creatures_log); + + // these channels will allow us to talk to each creature by 'name'/index let to_creature: ~[comm::chan>] = vec::mapi(set, fn@(ii: uint, col: color) -> comm::chan> { + // create each creature as a listener with a port, and + // give us a channel to talk to each ret do task::spawn_listener |from_rendezvous| { creature(ii, col, from_rendezvous, to_rendezvous, to_rendezvous_log); @@ -139,35 +144,17 @@ fn rendezvous(nn: uint, set: ~[color]) { } ); - let mut meetings = 0; let mut creatures_met = 0; - let mut creatures_present = 0; - - // use option type instead of initializing to junk? - let mut first_creature = { name: 0, color: Red }; - let mut second_creature = { name: 0, color: Red }; // set up meetings... - while meetings < nn { - let creature_req: creature_info = comm::recv(from_creatures); - creatures_met += 1; - - alt creatures_present { - 0 { - first_creature = creature_req; - creatures_present = 1; - } - 1 { - second_creature = creature_req; - comm::send(to_creature[first_creature.name], - some(second_creature)); - comm::send(to_creature[second_creature.name], - some(first_creature)); - creatures_present = 0; - meetings += 1; - } - _ { fail "too many creatures are here!" } - } + for nn.times { + let fst_creature: creature_info = comm::recv(from_creatures); + let snd_creature: creature_info = comm::recv(from_creatures); + + creatures_met += 2; + + comm::send(to_creature[fst_creature.name], some(snd_creature)); + comm::send(to_creature[snd_creature.name], some(fst_creature)); } // tell each creature to stop