]> git.lizzy.rs Git - rust.git/commitdiff
auto merge of #9140 : alexcrichton/rust/issue-9119, r=huonw
authorbors <bors@rust-lang.org>
Thu, 12 Sep 2013 18:56:00 +0000 (11:56 -0700)
committerbors <bors@rust-lang.org>
Thu, 12 Sep 2013 18:56:00 +0000 (11:56 -0700)
Closes #9119

25 files changed:
src/libextra/comm.rs
src/libextra/glob.rs
src/libextra/rl.rs
src/librust/rust.rs
src/librustc/metadata/decoder.rs
src/librustc/middle/check_match.rs
src/librustc/middle/mem_categorization.rs
src/librustc/middle/privacy.rs
src/librustc/middle/trans/_match.rs
src/librustc/middle/ty.rs
src/librustc/middle/typeck/check/_match.rs
src/librustc/middle/typeck/check/mod.rs
src/librusti/rusti.rs
src/libstd/fmt/mod.rs
src/libstd/unstable/dynamic_lib.rs
src/libsyntax/ast.rs
src/libsyntax/ext/base.rs
src/libsyntax/ext/ifmt.rs
src/rt/rust_builtin.cpp
src/rt/rustrt.def.in
src/test/compile-fail/ifmt-bad-format-args.rs [new file with mode: 0644]
src/test/compile-fail/ifmt-bad-format-args2.rs [new file with mode: 0644]
src/test/run-pass/ifmt.rs
src/test/run-pass/issue-9110.rs [new file with mode: 0644]
src/test/run-pass/rl-human-test.rs [new file with mode: 0644]

index 776e25cac8908447988c204aca86bea7c717db87..dc6f4964b317794d4cba77022ec2ee24043af92e 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -90,9 +90,55 @@ pub fn DuplexStream<T:Send,U:Send>()
      })
 }
 
+/// An extension of `pipes::stream` that provides synchronous message sending.
+pub struct SyncChan<T> { priv duplex_stream: DuplexStream<T, ()> }
+/// An extension of `pipes::stream` that acknowledges each message received.
+pub struct SyncPort<T> { priv duplex_stream: DuplexStream<(), T> }
+
+impl<T: Send> GenericChan<T> for SyncChan<T> {
+    fn send(&self, val: T) {
+        assert!(self.try_send(val), "SyncChan.send: receiving port closed");
+    }
+}
+
+impl<T: Send> GenericSmartChan<T> for SyncChan<T> {
+    /// Sends a message, or report if the receiver has closed the connection before receiving.
+    fn try_send(&self, val: T) -> bool {
+        self.duplex_stream.try_send(val) && self.duplex_stream.try_recv().is_some()
+    }
+}
+
+impl<T: Send> GenericPort<T> for SyncPort<T> {
+    fn recv(&self) -> T {
+        self.try_recv().expect("SyncPort.recv: sending channel closed")
+    }
+
+    fn try_recv(&self) -> Option<T> {
+        do self.duplex_stream.try_recv().map_move |val| {
+            self.duplex_stream.try_send(());
+            val
+        }
+    }
+}
+
+impl<T: Send> Peekable<T> for SyncPort<T> {
+    fn peek(&self) -> bool {
+        self.duplex_stream.peek()
+    }
+}
+
+/// Creates a stream whose channel, upon sending a message, blocks until the message is received.
+pub fn rendezvous<T: Send>() -> (SyncPort<T>, SyncChan<T>) {
+    let (chan_stream, port_stream) = DuplexStream();
+    (SyncPort { duplex_stream: port_stream }, SyncChan { duplex_stream: chan_stream })
+}
+
 #[cfg(test)]
 mod test {
-    use comm::DuplexStream;
+    use comm::{DuplexStream, rendezvous};
+    use std::rt::test::run_in_newsched_task;
+    use std::task::spawn_unlinked;
+
 
     #[test]
     pub fn DuplexStream1() {
@@ -104,4 +150,58 @@ pub fn DuplexStream1() {
         assert!(left.recv() == 123);
         assert!(right.recv() == ~"abc");
     }
+
+    #[test]
+    pub fn basic_rendezvous_test() {
+        let (port, chan) = rendezvous();
+
+        do spawn {
+            chan.send("abc");
+        }
+
+        assert!(port.recv() == "abc");
+    }
+
+    #[test]
+    fn recv_a_lot() {
+        // Rendezvous streams should be able to handle any number of messages being sent
+        do run_in_newsched_task {
+            let (port, chan) = rendezvous();
+            do spawn {
+                do 1000000.times { chan.send(()) }
+            }
+            do 1000000.times { port.recv() }
+        }
+    }
+
+    #[test]
+    fn send_and_fail_and_try_recv() {
+        let (port, chan) = rendezvous();
+        do spawn_unlinked {
+            chan.duplex_stream.send(()); // Can't access this field outside this module
+            fail!()
+        }
+        port.recv()
+    }
+
+    #[test]
+    fn try_send_and_recv_then_fail_before_ack() {
+        let (port, chan) = rendezvous();
+        do spawn_unlinked {
+            port.duplex_stream.recv();
+            fail!()
+        }
+        chan.try_send(());
+    }
+
+    #[test]
+    #[should_fail]
+    fn send_and_recv_then_fail_before_ack() {
+        let (port, chan) = rendezvous();
+        do spawn_unlinked {
+            port.duplex_stream.recv();
+            fail!()
+        }
+        chan.send(());
+    }
 }
index d82c1fd35c2ce8823aef51878db758038d1d0f3a..07386b41caac0bac312c0ddcafee8e9c6572184e 100644 (file)
@@ -147,8 +147,14 @@ enum PatternToken {
     Char(char),
     AnyChar,
     AnySequence,
-    AnyWithin(~[char]),
-    AnyExcept(~[char])
+    AnyWithin(~[CharSpecifier]),
+    AnyExcept(~[CharSpecifier])
+}
+
+#[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, IterBytes)]
+enum CharSpecifier {
+    SingleChar(char),
+    CharRange(char, char)
 }
 
 #[deriving(Eq)]
@@ -164,12 +170,15 @@ impl Pattern {
      * This function compiles Unix shell style patterns: `?` matches any single character,
      * `*` matches any (possibly empty) sequence of characters and `[...]` matches any character
      * inside the brackets, unless the first character is `!` in which case it matches any
-     * character except those between the `!` and the `]`.
+     * character except those between the `!` and the `]`. Character sequences can also specify
+     * ranges of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any character
+     * between 0 and 9 inclusive.
      *
      * The metacharacters `?`, `*`, `[`, `]` can be matched by using brackets (e.g. `[?]`).
      * When a `]` occurs immediately following `[` or `[!` then it is interpreted as
      * being part of, rather then ending, the character set, so `]` and NOT `]` can be
-     * matched by `[]]` and `[!]]` respectively.
+     * matched by `[]]` and `[!]]` respectively. The `-` character can be specified inside a
+     * character sequence pattern by placing it at the start or the end, e.g. `[abc-]`.
      *
      * When a `[` does not have a closing `]` before the end of the string then the `[` will
      * be treated literally.
@@ -199,7 +208,8 @@ pub fn new(pattern: &str) -> Pattern {
                         match chars.slice_from(i + 3).position_elem(&']') {
                             None => (),
                             Some(j) => {
-                                tokens.push(AnyExcept(chars.slice(i + 2, i + 3 + j).to_owned()));
+                                let cs = parse_char_specifiers(chars.slice(i + 2, i + 3 + j));
+                                tokens.push(AnyExcept(cs));
                                 i += j + 4;
                                 loop;
                             }
@@ -209,7 +219,8 @@ pub fn new(pattern: &str) -> Pattern {
                         match chars.slice_from(i + 2).position_elem(&']') {
                             None => (),
                             Some(j) => {
-                                tokens.push(AnyWithin(chars.slice(i + 1, i + 2 + j).to_owned()));
+                                let cs = parse_char_specifiers(chars.slice(i + 1, i + 2 + j));
+                                tokens.push(AnyWithin(cs));
                                 i += j + 3;
                                 loop;
                             }
@@ -335,15 +346,11 @@ fn matches_from(&self,
                         AnyChar => {
                             !require_literal(c)
                         }
-                        AnyWithin(ref chars) => {
-                            !require_literal(c) &&
-                            chars.iter()
-                                .rposition(|&e| chars_eq(e, c, options.case_sensitive)).is_some()
+                        AnyWithin(ref specifiers) => {
+                            !require_literal(c) && in_char_specifiers(*specifiers, c, options)
                         }
-                        AnyExcept(ref chars) => {
-                            !require_literal(c) &&
-                            chars.iter()
-                                .rposition(|&e| chars_eq(e, c, options.case_sensitive)).is_none()
+                        AnyExcept(ref specifiers) => {
+                            !require_literal(c) && !in_char_specifiers(*specifiers, c, options)
                         }
                         Char(c2) => {
                             chars_eq(c, c2, options.case_sensitive)
@@ -370,6 +377,63 @@ fn matches_from(&self,
 
 }
 
+fn parse_char_specifiers(s: &[char]) -> ~[CharSpecifier] {
+    let mut cs = ~[];
+    let mut i = 0;
+    while i < s.len() {
+        if i + 3 <= s.len() && s[i + 1] == '-' {
+            cs.push(CharRange(s[i], s[i + 2]));
+            i += 3;
+        } else {
+            cs.push(SingleChar(s[i]));
+            i += 1;
+        }
+    }
+    cs
+}
+
+fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptions) -> bool {
+
+    for &specifier in specifiers.iter() {
+        match specifier {
+            SingleChar(sc) => {
+                if chars_eq(c, sc, options.case_sensitive) {
+                    return true;
+                }
+            }
+            CharRange(start, end) => {
+
+                // FIXME: work with non-ascii chars properly (issue #1347)
+                if !options.case_sensitive && c.is_ascii() && start.is_ascii() && end.is_ascii() {
+
+                    let start = start.to_ascii().to_lower();
+                    let end = end.to_ascii().to_lower();
+
+                    let start_up = start.to_upper();
+                    let end_up = end.to_upper();
+
+                    // only allow case insensitive matching when
+                    // both start and end are within a-z or A-Z
+                    if start != start_up && end != end_up {
+                        let start = start.to_char();
+                        let end = end.to_char();
+                        let c = c.to_ascii().to_lower().to_char();
+                        if c >= start && c <= end {
+                            return true;
+                        }
+                    }
+                }
+
+                if c >= start && c <= end {
+                    return true;
+                }
+            }
+        }
+    }
+
+    false
+}
+
 /// A helper function to determine if two chars are (possibly case-insensitively) equal.
 fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool {
     if cfg!(windows) && path::windows::is_sep(a) && path::windows::is_sep(b) {
@@ -672,6 +736,54 @@ fn test_lots_of_files() {
         glob("/*/*/*/*").skip(10000).next();
     }
 
+    #[test]
+    fn test_range_pattern() {
+
+        let pat = Pattern::new("a[0-9]b");
+        for i in range(0, 10) {
+            assert!(pat.matches(fmt!("a%db", i)));
+        }
+        assert!(!pat.matches("a_b"));
+
+        let pat = Pattern::new("a[!0-9]b");
+        for i in range(0, 10) {
+            assert!(!pat.matches(fmt!("a%db", i)));
+        }
+        assert!(pat.matches("a_b"));
+
+        let pats = ["[a-z123]", "[1a-z23]", "[123a-z]"];
+        for &p in pats.iter() {
+            let pat = Pattern::new(p);
+            for c in "abcdefghijklmnopqrstuvwxyz".iter() {
+                assert!(pat.matches(c.to_str()));
+            }
+            for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ".iter() {
+                let options = MatchOptions {case_sensitive: false, .. MatchOptions::new()};
+                assert!(pat.matches_with(c.to_str(), options));
+            }
+            assert!(pat.matches("1"));
+            assert!(pat.matches("2"));
+            assert!(pat.matches("3"));
+        }
+
+        let pats = ["[abc-]", "[-abc]", "[a-c-]"];
+        for &p in pats.iter() {
+            let pat = Pattern::new(p);
+            assert!(pat.matches("a"));
+            assert!(pat.matches("b"));
+            assert!(pat.matches("c"));
+            assert!(pat.matches("-"));
+            assert!(!pat.matches("d"));
+        }
+
+        let pat = Pattern::new("[2-1]");
+        assert!(!pat.matches("1"));
+        assert!(!pat.matches("2"));
+
+        assert!(Pattern::new("[-]").matches("-"));
+        assert!(!Pattern::new("[!-]").matches("-"));
+    }
+
     #[test]
     fn test_unclosed_bracket() {
         // unclosed `[` should be treated literally
index 09ef7f22be5912b4d34c9d3f22c13e68b05d6844..74b7aea99787794ad9735519ca9a49023b285d5d 100644 (file)
@@ -8,13 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// FIXME #3921. This is unsafe because linenoise uses global mutable
-// state without mutexes.
-
 use std::c_str::ToCStr;
 use std::libc::{c_char, c_int};
-use std::local_data;
-use std::str;
+use std::{local_data, str, rt};
+use std::unstable::finally::Finally;
 
 #[cfg(stage0)]
 pub mod rustrt {
@@ -28,6 +25,9 @@ pub mod rustrt {
         fn linenoiseHistoryLoad(file: *c_char) -> c_int;
         fn linenoiseSetCompletionCallback(callback: *u8);
         fn linenoiseAddCompletion(completions: *(), line: *c_char);
+
+        fn rust_take_linenoise_lock();
+        fn rust_drop_linenoise_lock();
     }
 }
 
@@ -42,65 +42,107 @@ pub mod rustrt {
     externfn!(fn linenoiseHistoryLoad(file: *c_char) -> c_int)
     externfn!(fn linenoiseSetCompletionCallback(callback: extern "C" fn(*i8, *())))
     externfn!(fn linenoiseAddCompletion(completions: *(), line: *c_char))
+
+    externfn!(fn rust_take_linenoise_lock())
+    externfn!(fn rust_drop_linenoise_lock())
+}
+
+macro_rules! locked {
+    ($expr:expr) => {
+        unsafe {
+            // FIXME #9105: can't use a static mutex in pure Rust yet.
+            rustrt::rust_take_linenoise_lock();
+            let x = $expr;
+            rustrt::rust_drop_linenoise_lock();
+            x
+        }
+    }
 }
 
 /// Add a line to history
-pub unsafe fn add_history(line: &str) -> bool {
+pub fn add_history(line: &str) -> bool {
     do line.with_c_str |buf| {
-        rustrt::linenoiseHistoryAdd(buf) == 1 as c_int
+        (locked!(rustrt::linenoiseHistoryAdd(buf))) == 1 as c_int
     }
 }
 
 /// Set the maximum amount of lines stored
-pub unsafe fn set_history_max_len(len: int) -> bool {
-    rustrt::linenoiseHistorySetMaxLen(len as c_int) == 1 as c_int
+pub fn set_history_max_len(len: int) -> bool {
+    (locked!(rustrt::linenoiseHistorySetMaxLen(len as c_int))) == 1 as c_int
 }
 
 /// Save line history to a file
-pub unsafe fn save_history(file: &str) -> bool {
+pub fn save_history(file: &str) -> bool {
     do file.with_c_str |buf| {
-        rustrt::linenoiseHistorySave(buf) == 1 as c_int
+        // 0 on success, -1 on failure
+        (locked!(rustrt::linenoiseHistorySave(buf))) == 0 as c_int
     }
 }
 
 /// Load line history from a file
-pub unsafe fn load_history(file: &str) -> bool {
+pub fn load_history(file: &str) -> bool {
     do file.with_c_str |buf| {
-        rustrt::linenoiseHistoryLoad(buf) == 1 as c_int
+        // 0 on success, -1 on failure
+        (locked!(rustrt::linenoiseHistoryLoad(buf))) == 0 as c_int
     }
 }
 
 /// Print out a prompt and then wait for input and return it
-pub unsafe fn read(prompt: &str) -> Option<~str> {
+pub fn read(prompt: &str) -> Option<~str> {
     do prompt.with_c_str |buf| {
-        let line = rustrt::linenoise(buf);
+        let line = locked!(rustrt::linenoise(buf));
 
         if line.is_null() { None }
-        else { Some(str::raw::from_c_str(line)) }
+        else {
+            unsafe {
+                do (|| {
+                    Some(str::raw::from_c_str(line))
+                }).finally {
+                    // linenoise's return value is from strdup, so we
+                    // better not leak it.
+                    rt::global_heap::exchange_free(line);
+                }
+            }
+        }
     }
 }
 
 pub type CompletionCb = @fn(~str, @fn(~str));
 
-static complete_key: local_data::Key<@CompletionCb> = &local_data::Key;
-
-/// Bind to the main completion callback
-pub unsafe fn complete(cb: CompletionCb) {
-    local_data::set(complete_key, @cb);
-
-    extern fn callback(line: *c_char, completions: *()) {
-        do local_data::get(complete_key) |cb| {
-            let cb = **cb.unwrap();
-
-            unsafe {
-                do cb(str::raw::from_c_str(line)) |suggestion| {
-                    do suggestion.with_c_str |buf| {
-                        rustrt::linenoiseAddCompletion(completions, buf);
+static complete_key: local_data::Key<CompletionCb> = &local_data::Key;
+
+/// Bind to the main completion callback in the current task.
+///
+/// The completion callback should not call any `extra::rl` functions
+/// other than the closure that it receives as its second
+/// argument. Calling such a function will deadlock on the mutex used
+/// to ensure that the calls are thread-safe.
+pub fn complete(cb: CompletionCb) {
+    local_data::set(complete_key, cb);
+
+    extern fn callback(c_line: *c_char, completions: *()) {
+        do local_data::get(complete_key) |opt_cb| {
+            // only fetch completions if a completion handler has been
+            // registered in the current task.
+            match opt_cb {
+                None => {},
+                Some(cb) => {
+                    let line = unsafe { str::raw::from_c_str(c_line) };
+                    do (*cb)(line) |suggestion| {
+                        do suggestion.with_c_str |buf| {
+                            // This isn't locked, because `callback` gets
+                            // called inside `rustrt::linenoise`, which
+                            // *is* already inside the mutex, so
+                            // re-locking would be a deadlock.
+                            unsafe {
+                                rustrt::linenoiseAddCompletion(completions, buf);
+                            }
+                        }
                     }
                 }
             }
         }
     }
 
-    rustrt::linenoiseSetCompletionCallback(callback);
+    locked!(rustrt::linenoiseSetCompletionCallback(callback));
 }
index b44fb100000d6fadb7630456eed317a79d9b74b9..7933d33d140f06c95d82aca2c85f900e98ef8cc5 100644 (file)
@@ -60,20 +60,14 @@ struct Command<'self> {
     usage_full: UsageSource<'self>,
 }
 
-static NUM_OF_COMMANDS: uint = 7;
-
-// FIXME(#7617): should just be &'static [Command<'static>]
-// but mac os doesn't seem to like that and tries to loop
-// past the end of COMMANDS in usage thus passing garbage
-// to str::repeat and eventually malloc and crashing.
-static COMMANDS: [Command<'static>, .. NUM_OF_COMMANDS] = [
-    Command{
+static COMMANDS: &'static [Command<'static>] = &'static [
+    Command {
         cmd: "build",
         action: CallMain("rustc", rustc::main_args),
         usage_line: "compile rust source files",
         usage_full: UsgCall(rustc_help),
     },
-    Command{
+    Command {
         cmd: "run",
         action: Call(cmd_run),
         usage_line: "build an executable, and run it",
@@ -83,7 +77,7 @@ struct Command<'self> {
             \n\nUsage:\trust run <filename> [<arguments>...]"
         )
     },
-    Command{
+    Command {
         cmd: "test",
         action: Call(cmd_test),
         usage_line: "build a test executable, and run it",
@@ -93,25 +87,25 @@ struct Command<'self> {
             ./<filestem>test~\"\n\nUsage:\trust test <filename>"
         )
     },
-    Command{
+    Command {
         cmd: "doc",
         action: CallMain("rustdoc", rustdoc::main_args),
         usage_line: "generate documentation from doc comments",
         usage_full: UsgCall(rustdoc::config::usage),
     },
-    Command{
+    Command {
         cmd: "pkg",
         action: CallMain("rustpkg", rustpkg::main_args),
         usage_line: "download, build, install rust packages",
         usage_full: UsgCall(rustpkg::usage::general),
     },
-    Command{
+    Command {
         cmd: "sketch",
         action: CallMain("rusti", rusti::main_args),
         usage_line: "run a rust interpreter",
         usage_full: UsgStr("\nUsage:\trusti"),
     },
-    Command{
+    Command {
         cmd: "help",
         action: Call(cmd_help),
         usage_line: "show detailed usage of a command",
index debbc7591d4a4ca9ecec41494f5755f98c610de0..87108530c757e2a74a348693cc6107477a8a9439 100644 (file)
@@ -1202,10 +1202,11 @@ pub fn get_struct_fields(intr: @ident_interner, cdata: Cmd, id: ast::NodeId)
     do reader::tagged_docs(item, tag_item_field) |an_item| {
         let f = item_family(an_item);
         if f == PublicField || f == PrivateField || f == InheritedField {
+            // FIXME #6993: name should be of type Name, not Ident
             let name = item_name(intr, an_item);
             let did = item_def_id(an_item, cdata);
             result.push(ty::field_ty {
-                ident: name,
+                name: name.name,
                 id: did, vis:
                 struct_field_family_to_visibility(f),
             });
@@ -1215,7 +1216,7 @@ pub fn get_struct_fields(intr: @ident_interner, cdata: Cmd, id: ast::NodeId)
     do reader::tagged_docs(item, tag_item_unnamed_field) |an_item| {
         let did = item_def_id(an_item, cdata);
         result.push(ty::field_ty {
-            ident: special_idents::unnamed_field,
+            name: special_idents::unnamed_field.name,
             id: did,
             vis: ast::inherited,
         });
index 16338b25bf49aa73a8c36c63fc3f2b8956349cd6..2a38492a6e58626800a41bd765f0e380c509aa68 100644 (file)
@@ -700,7 +700,7 @@ pub fn specialize(cx: &MatchCheckCtxt,
                         }
                         let args = class_fields.iter().map(|class_field| {
                             match flds.iter().find(|f|
-                                            f.ident == class_field.ident) {
+                                            f.ident.name == class_field.name) {
                                 Some(f) => f.pat,
                                 _ => wild()
                             }
index 63b63d8d69142403bb8be776c55cdd74a3d5e8ff..0e3c10ef21471c00fd990b6cb22aa2165c760e39 100644 (file)
@@ -1059,6 +1059,7 @@ pub fn region_to_str(&self, r: ty::Region) -> ~str {
 /// an enum to determine which variant is in use.
 pub fn field_mutbl(tcx: ty::ctxt,
                    base_ty: ty::t,
+                   // FIXME #6993: change type to Name
                    f_name: ast::Ident,
                    node_id: ast::NodeId)
                 -> Option<ast::Mutability> {
@@ -1067,7 +1068,7 @@ pub fn field_mutbl(tcx: ty::ctxt,
       ty::ty_struct(did, _) => {
         let r = ty::lookup_struct_fields(tcx, did);
         for fld in r.iter() {
-            if fld.ident == f_name {
+            if fld.name == f_name.name {
                 return Some(ast::MutImmutable);
             }
         }
@@ -1077,7 +1078,7 @@ pub fn field_mutbl(tcx: ty::ctxt,
           ast::DefVariant(_, variant_id, _) => {
             let r = ty::lookup_struct_fields(tcx, variant_id);
             for fld in r.iter() {
-                if fld.ident == f_name {
+                if fld.name == f_name.name {
                     return Some(ast::MutImmutable);
                 }
             }
index 085925f97a46be8ffb4a526e98a3bb6a377d597f..b6b03d8369a44528d60c4c62888437272729a016 100644 (file)
@@ -203,10 +203,11 @@ fn local_item_is_private(&mut self, span: Span, item_id: NodeId) -> bool {
     }
 
     // Checks that a private field is in scope.
+    // FIXME #6993: change type (and name) from Ident to Name
     fn check_field(&mut self, span: Span, id: ast::DefId, ident: ast::Ident) {
         let fields = ty::lookup_struct_fields(self.tcx, id);
         for field in fields.iter() {
-            if field.ident.name != ident.name { loop; }
+            if field.name != ident.name { loop; }
             if field.vis == private {
                 self.tcx.sess.span_err(span, fmt!("field `%s` is private",
                                              token::ident_to_str(&ident)));
index 244e0e6e85d48c9c3f85f96aef7e69b54fe28d14..62fbdc41b0e7046f34187a15e71b725ce7fd1d2d 100644 (file)
@@ -672,7 +672,7 @@ fn enter_opt<'r>(bcx: @mut Block,
                     let r = ty::lookup_struct_fields(tcx, struct_id);
                     for field in r.iter() {
                             match field_pats.iter().find(|p| p.ident.name
-                                                         == field.ident.name) {
+                                                         == field.name) {
                                 None => reordered_patterns.push(dummy),
                                 Some(fp) => reordered_patterns.push(fp.pat)
                             }
index 0e57bdc53215b56dbb5c01ff26a888e0bd993164..33870ed6acb06597e41ff094aa949db0b8816ab2 100644 (file)
@@ -156,7 +156,7 @@ pub enum SelfMode {
 }
 
 pub struct field_ty {
-    ident: Ident,
+    name: Name,
     id: DefId,
     vis: ast::visibility,
 }
@@ -1757,7 +1757,7 @@ fn type_is_newtype_immediate(cx: ctxt, ty: t) -> bool {
         ty_struct(def_id, ref substs) => {
             let fields = struct_fields(cx, def_id, substs);
             fields.len() == 1 &&
-                fields[0].ident == token::special_idents::unnamed_field &&
+                fields[0].ident.name == token::special_idents::unnamed_field.name &&
                 type_is_immediate(cx, fields[0].mt.ty)
         }
         _ => false
@@ -4227,15 +4227,15 @@ fn struct_field_tys(fields: &[@struct_field]) -> ~[field_ty] {
         match field.node.kind {
             named_field(ident, visibility) => {
                 field_ty {
-                    ident: ident,
+                    name: ident.name,
                     id: ast_util::local_def(field.node.id),
                     vis: visibility,
                 }
             }
             unnamed_field => {
                 field_ty {
-                    ident:
-                        syntax::parse::token::special_idents::unnamed_field,
+                    name:
+                        syntax::parse::token::special_idents::unnamed_field.name,
                     id: ast_util::local_def(field.node.id),
                     vis: ast::public,
                 }
@@ -4250,7 +4250,8 @@ pub fn struct_fields(cx: ctxt, did: ast::DefId, substs: &substs)
                      -> ~[field] {
     do lookup_struct_fields(cx, did).map |f| {
        field {
-            ident: f.ident,
+            // FIXME #6993: change type of field to Name and get rid of new()
+            ident: ast::Ident::new(f.name),
             mt: mt {
                 ty: lookup_field_type(cx, did, f.id, substs),
                 mutbl: MutImmutable
index 4c5ee1ca2174143c5cb6339caf4a0de381c49a0a..196057e09f762d84fc7f26f15e6a19dad971500c 100644 (file)
@@ -21,6 +21,7 @@
 use std::hashmap::{HashMap, HashSet};
 use syntax::ast;
 use syntax::ast_util;
+use syntax::parse::token;
 use syntax::codemap::Span;
 use syntax::print::pprust;
 
@@ -296,7 +297,7 @@ pub fn check_struct_pat_fields(pcx: &pat_ctxt,
     // Index the class fields.
     let mut field_map = HashMap::new();
     for (i, class_field) in class_fields.iter().enumerate() {
-        field_map.insert(class_field.ident.name, i);
+        field_map.insert(class_field.name, i);
     }
 
     // Typecheck each field.
@@ -333,7 +334,7 @@ pub fn check_struct_pat_fields(pcx: &pat_ctxt,
             }
             tcx.sess.span_err(span,
                               fmt!("pattern does not mention field `%s`",
-                                   tcx.sess.str_of(field.ident)));
+                                   token::interner_get(field.name)));
         }
     }
 }
index 265e19fdaa730214a03bc87554628619056e3082..b689090d3fd3e659a44ee8bd7fffa8d166a65db9 100644 (file)
@@ -1120,7 +1120,7 @@ pub fn lookup_field_ty(tcx: ty::ctxt,
                        fieldname: ast::Name,
                        substs: &ty::substs) -> Option<ty::t> {
 
-    let o_field = items.iter().find(|f| f.ident.name == fieldname);
+    let o_field = items.iter().find(|f| f.name == fieldname);
     do o_field.map() |f| {
         ty::lookup_field_type(tcx, class_id, f.id, substs)
     }
@@ -2018,7 +2018,7 @@ fn check_struct_or_variant_fields(fcx: @mut FnCtxt,
         let mut class_field_map = HashMap::new();
         let mut fields_found = 0;
         for field in field_types.iter() {
-            class_field_map.insert(field.ident.name, (field.id, false));
+            class_field_map.insert(field.name, (field.id, false));
         }
 
         let mut error_happened = false;
@@ -2070,7 +2070,7 @@ fn check_struct_or_variant_fields(fcx: @mut FnCtxt,
             if fields_found < field_types.len() {
                 let mut missing_fields = ~[];
                 for class_field in field_types.iter() {
-                    let name = class_field.ident.name;
+                    let name = class_field.name;
                     let (_, seen) = *class_field_map.get(&name);
                     if !seen {
                         missing_fields.push(
index 5bd941759f4d7e81c1473dac3c66335500de2f8b..8d61a971157fc35e69687b423794dac6c797753e 100644 (file)
@@ -355,12 +355,12 @@ fn compile_crate(src_filename: ~str, binary: ~str) -> Option<bool> {
 /// None if no input was read (e.g. EOF was reached).
 fn get_line(use_rl: bool, prompt: &str) -> Option<~str> {
     if use_rl {
-        let result = unsafe { rl::read(prompt) };
+        let result = rl::read(prompt);
 
         match result {
             None => None,
             Some(line) => {
-                unsafe { rl::add_history(line) };
+                rl::add_history(line);
                 Some(line)
             }
         }
@@ -525,14 +525,12 @@ pub fn main_args(args: &[~str]) {
         println("unstable. If you encounter problems, please use the");
         println("compiler instead. Type :help for help.");
 
-        unsafe {
-            do rl::complete |line, suggest| {
-                if line.starts_with(":") {
-                    suggest(~":clear");
-                    suggest(~":exit");
-                    suggest(~":help");
-                    suggest(~":load");
-                }
+        do rl::complete |line, suggest| {
+            if line.starts_with(":") {
+                suggest(~":clear");
+                suggest(~":exit");
+                suggest(~":help");
+                suggest(~":load");
             }
         }
     }
index 7d5033e3a6ad1579339ffe7077db16d8c0c3cc52..c7ab508ea6ec67dbf0a318989bd698008a64d8d3 100644 (file)
@@ -36,7 +36,7 @@
 format!("Hello")                  // => ~"Hello"
 format!("Hello, {:s}!", "world")  // => ~"Hello, world!"
 format!("The number is {:d}", 1)  // => ~"The number is 1"
-format!("{}", ~[3, 4])            // => ~"~[3, 4]"
+format!("{:?}", ~[3, 4])          // => ~"~[3, 4]"
 format!("{value}", value=4)       // => ~"4"
 format!("{} {}", 1, 2)            // => ~"1 2"
 ~~~
@@ -363,6 +363,32 @@ pub struct Argument<'self> {
     priv value: &'self util::Void,
 }
 
+impl<'self> Arguments<'self> {
+    /// When using the format_args!() macro, this function is used to generate the
+    /// Arguments structure. The compiler inserts an `unsafe` block to call this,
+    /// which is valid because the compiler performs all necessary validation to
+    /// ensure that the resulting call to format/write would be safe.
+    #[doc(hidden)] #[inline]
+    pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
+                          args: &'a [Argument<'a>]) -> Arguments<'a> {
+        Arguments{ fmt: cast::transmute(fmt), args: args }
+    }
+}
+
+/// This structure represents a safely precompiled version of a format string
+/// and its arguments. This cannot be generated at runtime because it cannot
+/// safely be done so, so no constructors are given and the fields are private
+/// to prevent modification.
+///
+/// The `format_args!` macro will safely create an instance of this structure
+/// and pass it to a user-supplied function. The macro validates the format
+/// string at compile-time so usage of the `write` and `format` functions can
+/// be safely performed.
+pub struct Arguments<'self> {
+    priv fmt: &'self [rt::Piece<'self>],
+    priv args: &'self [Argument<'self>],
+}
+
 /// When a format is not otherwise specified, types are formatted by ascribing
 /// to this trait. There is not an explicit way of selecting this trait to be
 /// used for formatting, it is only if no other format is specified.
@@ -410,6 +436,26 @@ pub struct Argument<'self> {
 /// and a list of arguments. The arguments will be formatted according to the
 /// specified format string into the output stream provided.
 ///
+/// # Arguments
+///
+///   * output - the buffer to write output to
+///   * args - the precompiled arguments generated by `format_args!`
+///
+/// # Example
+///
+/// ~~~{.rust}
+/// use std::fmt;
+/// let w: &mut io::Writer = ...;
+/// format_args!(|args| { fmt::write(w, args) }, "Hello, {}!", "world");
+/// ~~~
+pub fn write(output: &mut io::Writer, args: &Arguments) {
+    unsafe { write_unsafe(output, args.fmt, args.args) }
+}
+
+/// The `write_unsafe` function takes an output stream, a precompiled format
+/// string, and a list of arguments. The arguments will be formatted according
+/// to the specified format string into the output stream provided.
+///
 /// See the documentation for `format` for why this function is unsafe and care
 /// should be taken if calling it manually.
 ///
@@ -426,8 +472,9 @@ pub struct Argument<'self> {
 ///
 /// Note that this function assumes that there are enough arguments for the
 /// format string.
-pub unsafe fn write(output: &mut io::Writer,
-                    fmt: &[rt::Piece], args: &[Argument]) {
+pub unsafe fn write_unsafe(output: &mut io::Writer,
+                           fmt: &[rt::Piece],
+                           args: &[Argument]) {
     let mut formatter = Formatter {
         flags: 0,
         width: None,
@@ -446,6 +493,25 @@ pub unsafe fn write(output: &mut io::Writer,
 /// The format function takes a precompiled format string and a list of
 /// arguments, to return the resulting formatted string.
 ///
+/// # Arguments
+///
+///   * args - a structure of arguments generated via the `format_args!` macro.
+///            Because this structure can only be safely generated at
+///            compile-time, this function is safe.
+///
+/// # Example
+///
+/// ~~~{.rust}
+/// use std::fmt;
+/// let s = format_args!(fmt::format, "Hello, {}!", "world");
+/// assert_eq!(s, "Hello, world!");
+/// ~~~
+pub fn format(args: &Arguments) -> ~str {
+    unsafe { format_unsafe(args.fmt, args.args) }
+}
+
+/// The unsafe version of the formatting function.
+///
 /// This is currently an unsafe function because the types of all arguments
 /// aren't verified by immediate callers of this function. This currently does
 /// not validate that the correct types of arguments are specified for each
@@ -465,9 +531,9 @@ pub unsafe fn write(output: &mut io::Writer,
 ///
 /// Note that this function assumes that there are enough arguments for the
 /// format string.
-pub unsafe fn format(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
+pub unsafe fn format_unsafe(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
     let mut output = MemWriter::new();
-    write(&mut output as &mut io::Writer, fmt, args);
+    write_unsafe(&mut output as &mut io::Writer, fmt, args);
     return str::from_utf8_owned(output.inner());
 }
 
@@ -740,7 +806,7 @@ fn with_padding(&mut self, padding: uint,
 
 /// This is a function which calls are emitted to by the compiler itself to
 /// create the Argument structures that are passed into the `format` function.
-#[doc(hidden)]
+#[doc(hidden)] #[inline]
 pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
                        t: &'a T) -> Argument<'a> {
     unsafe {
@@ -753,14 +819,14 @@ pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
 
 /// When the compiler determines that the type of an argument *must* be a string
 /// (such as for select), then it invokes this method.
-#[doc(hidden)]
+#[doc(hidden)] #[inline]
 pub fn argumentstr<'a>(s: &'a &str) -> Argument<'a> {
     argument(String::fmt, s)
 }
 
 /// When the compiler determines that the type of an argument *must* be a uint
 /// (such as for plural), then it invokes this method.
-#[doc(hidden)]
+#[doc(hidden)] #[inline]
 pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> {
     argument(Unsigned::fmt, s)
 }
@@ -899,14 +965,8 @@ fn fmt(t: &*T, f: &mut Formatter) {
         }
     }
 }
-
 impl<T> Pointer for *mut T {
-    fn fmt(t: &*mut T, f: &mut Formatter) {
-        f.flags |= 1 << (parse::FlagAlternate as uint);
-        do ::uint::to_str_bytes(*t as uint, 16) |buf| {
-            f.pad_integral(buf, "0x", true);
-        }
-    }
+    fn fmt(t: &*mut T, f: &mut Formatter) { Pointer::fmt(&(*t as *T), f) }
 }
 
 // Implementation of Default for various core types
@@ -940,7 +1000,6 @@ fn fmt(me: &$ty, f: &mut Formatter) {
 impl<T> Default for *T {
     fn fmt(me: &*T, f: &mut Formatter) { Pointer::fmt(me, f) }
 }
-
 impl<T> Default for *mut T {
     fn fmt(me: &*mut T, f: &mut Formatter) { Pointer::fmt(me, f) }
 }
index db106de76d94e4ce5eb97d3097da2a2fdcd378ee..d8a07eeb8b7d3d1ff7b15179b04336624d67d12d 100644 (file)
@@ -90,7 +90,8 @@ mod test {
     use libc;
 
     #[test]
-    #[ignore(cfg(windows))] // FIXME #8818
+    // #[ignore(cfg(windows))] // FIXME #8818
+    #[ignore] // FIXME #9137 this library isn't thread-safe
     fn test_loading_cosine() {
         // The math library does not need to be loaded since it is already
         // statically linked in
@@ -121,6 +122,7 @@ fn test_loading_cosine() {
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "macos")]
     #[cfg(target_os = "freebsd")]
+    #[ignore] // FIXME #9137 this library isn't thread-safe
     fn test_errors_do_not_crash() {
         // Open /dev/null as a library to get an error, and make sure
         // that only causes an error, and not a crash.
index f5de683cb977949980d55d7b79038d7eac93b317..7e4cbf8e97511b2931efeef21ef715601b17ae43 100644 (file)
@@ -47,7 +47,8 @@ fn eq(&self, other: &Ident) -> bool {
             // if it should be non-hygienic (most things are), just compare the
             // 'name' fields of the idents. Or, even better, replace the idents
             // with Name's.
-            fail!(fmt!("not allowed to compare these idents: %?, %?", self, other));
+            fail!(fmt!("not allowed to compare these idents: %?, %?. Probably \
+                       related to issue #6993", self, other));
         }
     }
     fn ne(&self, other: &Ident) -> bool {
index 48a1364668688a58c24937d8e1aa2478eb6f6395..284801ab123c923f7e038b92742b466e656f58bf 100644 (file)
@@ -161,6 +161,8 @@ fn builtin_item_tt_no_ctxt(f: SyntaxExpanderTTItemFunNoCtxt) -> @Transformer {
                             builtin_normal_tt_no_ctxt(ext::ifmt::expand_write));
     syntax_expanders.insert(intern(&"writeln"),
                             builtin_normal_tt_no_ctxt(ext::ifmt::expand_writeln));
+    syntax_expanders.insert(intern(&"format_args"),
+                            builtin_normal_tt_no_ctxt(ext::ifmt::expand_format_args));
     syntax_expanders.insert(
         intern(&"auto_encode"),
         @SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
index 486069db4f0c4f4f85809b379c0b2c57b969b537..2f040ef251923583e737c5f8c3115e569449e637 100644 (file)
@@ -60,7 +60,7 @@ fn parse_args(&mut self, sp: Span,
         let p = rsparse::new_parser_from_tts(self.ecx.parse_sess(),
                                              self.ecx.cfg(),
                                              tts.to_owned());
-        // If we want a leading expression (for ifmtf), parse it here
+        // If we want a leading expression, parse it here
         let extra = if leading_expr {
             let e = Some(p.parse_expr());
             if !p.eat(&token::COMMA) {
@@ -341,12 +341,18 @@ fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr {
             ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"),
               self.ecx.ident_of("parse"), self.ecx.ident_of(s)]
         };
-        let none = || {
-            let p = self.ecx.path(sp, ~[self.ecx.ident_of("None")]);
-            self.ecx.expr_path(p)
-        };
+        let none = self.ecx.path_global(sp, ~[
+                self.ecx.ident_of("std"),
+                self.ecx.ident_of("option"),
+                self.ecx.ident_of("None")]);
+        let none = self.ecx.expr_path(none);
         let some = |e: @ast::Expr| {
-            self.ecx.expr_call_ident(sp, self.ecx.ident_of("Some"), ~[e])
+            let p = self.ecx.path_global(sp, ~[
+                self.ecx.ident_of("std"),
+                self.ecx.ident_of("option"),
+                self.ecx.ident_of("Some")]);
+            let p = self.ecx.expr_path(p);
+            self.ecx.expr_call(sp, p, ~[e])
         };
         let trans_count = |c: parse::Count| {
             match c {
@@ -397,7 +403,7 @@ fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr {
                 parse::Plural(offset, ref arms, ref default) => {
                     let offset = match offset {
                         Some(i) => { some(self.ecx.expr_uint(sp, i)) }
-                        None => { none() }
+                        None => { none.clone() }
                     };
                     let arms = arms.iter().map(|arm| {
                         let p = self.ecx.path_global(sp, rtpath("PluralArm"));
@@ -522,7 +528,7 @@ fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr {
 
                 // Translate the method (if any)
                 let method = match arg.method {
-                    None => { none() }
+                    None => { none.clone() }
                     Some(ref m) => {
                         let m = trans_method(*m);
                         some(self.ecx.expr_addr_of(sp, m))
@@ -541,7 +547,7 @@ fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr {
 
     /// Actually builds the expression which the ifmt! block will be expanded
     /// to
-    fn to_expr(&self, extra: Option<@ast::Expr>, f: &str) -> @ast::Expr {
+    fn to_expr(&self, extra: Option<@ast::Expr>, f: Option<&str>) -> @ast::Expr {
         let mut lets = ~[];
         let mut locals = ~[];
         let mut names = vec::from_fn(self.name_positions.len(), |_| None);
@@ -556,21 +562,19 @@ fn to_expr(&self, extra: Option<@ast::Expr>, f: &str) -> @ast::Expr {
         // Next, build up the static array which will become our precompiled
         // format "string"
         let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
+        let piece_ty = self.ecx.ty_path(self.ecx.path_all(
+                self.fmtsp,
+                true, ~[
+                    self.ecx.ident_of("std"),
+                    self.ecx.ident_of("fmt"),
+                    self.ecx.ident_of("rt"),
+                    self.ecx.ident_of("Piece"),
+                ],
+                Some(self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("static"))),
+                ~[]
+            ), None);
         let ty = ast::ty_fixed_length_vec(
-            self.ecx.ty_mt(
-                self.ecx.ty_path(self.ecx.path_all(
-                    self.fmtsp,
-                    true, ~[
-                        self.ecx.ident_of("std"),
-                        self.ecx.ident_of("fmt"),
-                        self.ecx.ident_of("rt"),
-                        self.ecx.ident_of("Piece"),
-                    ],
-                    Some(self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("static"))),
-                    ~[]
-                ), None),
-                ast::MutImmutable
-            ),
+            self.ecx.ty_mt(piece_ty.clone(), ast::MutImmutable),
             self.ecx.expr_uint(self.fmtsp, self.pieces.len())
         );
         let ty = self.ecx.ty(self.fmtsp, ty);
@@ -596,7 +600,8 @@ fn to_expr(&self, extra: Option<@ast::Expr>, f: &str) -> @ast::Expr {
             let name = self.ecx.ident_of(fmt!("__arg%u", i));
             let e = self.ecx.expr_addr_of(e.span, e);
             lets.push(self.ecx.stmt_let(e.span, false, name, e));
-            locals.push(self.format_arg(e.span, Left(i), name));
+            locals.push(self.format_arg(e.span, Left(i),
+                                        self.ecx.expr_ident(e.span, name)));
         }
         for (&name, &e) in self.names.iter() {
             if !self.name_types.contains_key(&name) { loop }
@@ -605,48 +610,83 @@ fn to_expr(&self, extra: Option<@ast::Expr>, f: &str) -> @ast::Expr {
             let e = self.ecx.expr_addr_of(e.span, e);
             lets.push(self.ecx.stmt_let(e.span, false, lname, e));
             names[*self.name_positions.get(&name)] =
-                Some(self.format_arg(e.span, Right(name), lname));
+                Some(self.format_arg(e.span, Right(name),
+                                     self.ecx.expr_ident(e.span, lname)));
         }
 
         let args = names.move_iter().map(|a| a.unwrap());
         let mut args = locals.move_iter().chain(args);
 
-        let mut fmt_args = match extra {
-            Some(e) => ~[e], None => ~[]
-        };
-        fmt_args.push(self.ecx.expr_ident(self.fmtsp, static_name));
-        fmt_args.push(self.ecx.expr_vec(self.fmtsp, args.collect()));
+        let result = match f {
+            // Invocation of write!()/format!(), call the function and we're
+            // done.
+            Some(f) => {
+                let mut fmt_args = match extra {
+                    Some(e) => ~[e], None => ~[]
+                };
+                fmt_args.push(self.ecx.expr_ident(self.fmtsp, static_name));
+                fmt_args.push(self.ecx.expr_vec_slice(self.fmtsp,
+                                                      args.collect()));
 
-        // Next, build up the actual call to the {s,f}printf function.
-        let result = self.ecx.expr_call_global(self.fmtsp, ~[
-                self.ecx.ident_of("std"),
-                self.ecx.ident_of("fmt"),
-                self.ecx.ident_of(f),
-            ], fmt_args);
-
-        // sprintf is unsafe, but we just went through a lot of work to
-        // validate that our call is save, so inject the unsafe block for the
-        // user.
-        let result = self.ecx.expr_block(ast::Block {
-           view_items: ~[],
-           stmts: ~[],
-           expr: Some(result),
-           id: ast::DUMMY_NODE_ID,
-           rules: ast::UnsafeBlock(ast::CompilerGenerated),
-           span: self.fmtsp,
-        });
-
-        self.ecx.expr_block(self.ecx.block(self.fmtsp, lets, Some(result)))
+                let result = self.ecx.expr_call_global(self.fmtsp, ~[
+                        self.ecx.ident_of("std"),
+                        self.ecx.ident_of("fmt"),
+                        self.ecx.ident_of(f),
+                    ], fmt_args);
+
+                // sprintf is unsafe, but we just went through a lot of work to
+                // validate that our call is save, so inject the unsafe block
+                // for the user.
+                self.ecx.expr_block(ast::Block {
+                   view_items: ~[],
+                   stmts: ~[],
+                   expr: Some(result),
+                   id: ast::DUMMY_NODE_ID,
+                   rules: ast::UnsafeBlock(ast::CompilerGenerated),
+                   span: self.fmtsp,
+                })
+            }
+
+            // Invocation of format_args!()
+            None => {
+                let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
+                let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
+                let result = self.ecx.expr_call_global(self.fmtsp, ~[
+                        self.ecx.ident_of("std"),
+                        self.ecx.ident_of("fmt"),
+                        self.ecx.ident_of("Arguments"),
+                        self.ecx.ident_of("new"),
+                    ], ~[fmt, args]);
+
+                // We did all the work of making sure that the arguments
+                // structure is safe, so we can safely have an unsafe block.
+                let result = self.ecx.expr_block(ast::Block {
+                   view_items: ~[],
+                   stmts: ~[],
+                   expr: Some(result),
+                   id: ast::DUMMY_NODE_ID,
+                   rules: ast::UnsafeBlock(ast::CompilerGenerated),
+                   span: self.fmtsp,
+                });
+                let extra = extra.unwrap();
+                let resname = self.ecx.ident_of("__args");
+                lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
+                let res = self.ecx.expr_ident(self.fmtsp, resname);
+                self.ecx.expr_call(extra.span, extra, ~[
+                        self.ecx.expr_addr_of(extra.span, res)])
+            }
+        };
+        self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
+                                           Some(result)))
     }
 
-    fn format_arg(&self, sp: Span, arg: Either<uint, @str>,
-                  ident: ast::Ident) -> @ast::Expr {
-        let ty = match arg {
+    fn format_arg(&self, sp: Span, argno: Either<uint, @str>,
+                  arg: @ast::Expr) -> @ast::Expr {
+        let ty = match argno {
             Left(i) => self.arg_types[i].unwrap(),
             Right(s) => *self.name_types.get(&s)
         };
 
-        let argptr = self.ecx.expr_ident(sp, ident);
         let fmt_trait = match ty {
             Unknown => "Default",
             Known(tyname) => {
@@ -675,14 +715,14 @@ fn format_arg(&self, sp: Span, arg: Either<uint, @str>,
                         self.ecx.ident_of("std"),
                         self.ecx.ident_of("fmt"),
                         self.ecx.ident_of("argumentstr"),
-                    ], ~[argptr])
+                    ], ~[arg])
             }
             Unsigned => {
                 return self.ecx.expr_call_global(sp, ~[
                         self.ecx.ident_of("std"),
                         self.ecx.ident_of("fmt"),
                         self.ecx.ident_of("argumentuint"),
-                    ], ~[argptr])
+                    ], ~[arg])
             }
         };
 
@@ -696,28 +736,33 @@ fn format_arg(&self, sp: Span, arg: Either<uint, @str>,
                 self.ecx.ident_of("std"),
                 self.ecx.ident_of("fmt"),
                 self.ecx.ident_of("argument"),
-            ], ~[self.ecx.expr_path(format_fn), argptr])
+            ], ~[self.ecx.expr_path(format_fn), arg])
     }
 }
 
 pub fn expand_format(ecx: @ExtCtxt, sp: Span,
                      tts: &[ast::token_tree]) -> base::MacResult {
-    expand_ifmt(ecx, sp, tts, false, false, "format")
+    expand_ifmt(ecx, sp, tts, false, false, Some("format_unsafe"))
 }
 
 pub fn expand_write(ecx: @ExtCtxt, sp: Span,
                     tts: &[ast::token_tree]) -> base::MacResult {
-    expand_ifmt(ecx, sp, tts, true, false, "write")
+    expand_ifmt(ecx, sp, tts, true, false, Some("write_unsafe"))
 }
 
 pub fn expand_writeln(ecx: @ExtCtxt, sp: Span,
                       tts: &[ast::token_tree]) -> base::MacResult {
-    expand_ifmt(ecx, sp, tts, true, true, "write")
+    expand_ifmt(ecx, sp, tts, true, true, Some("write_unsafe"))
+}
+
+pub fn expand_format_args(ecx: @ExtCtxt, sp: Span,
+                          tts: &[ast::token_tree]) -> base::MacResult {
+    expand_ifmt(ecx, sp, tts, true, false, None)
 }
 
 fn expand_ifmt(ecx: @ExtCtxt, sp: Span, tts: &[ast::token_tree],
                leading_arg: bool, append_newline: bool,
-               function: &str) -> base::MacResult {
+               function: Option<&str>) -> base::MacResult {
     let mut cx = Context {
         ecx: ecx,
         args: ~[],
index 03a17d2c2ef97360a15c5850ea759147ff9f7c84..1871e7f36b363a376591db071278e86e636a5280 100644 (file)
@@ -633,6 +633,18 @@ rust_drop_env_lock() {
     env_lock.unlock();
 }
 
+static lock_and_signal linenoise_lock;
+
+extern "C" CDECL void
+rust_take_linenoise_lock() {
+    linenoise_lock.lock();
+}
+
+extern "C" CDECL void
+rust_drop_linenoise_lock() {
+    linenoise_lock.unlock();
+}
+
 extern "C" CDECL unsigned int
 rust_valgrind_stack_register(void *start, void *end) {
   return VALGRIND_STACK_REGISTER(start, end);
index bf3500e4c724e12548cd245955d8a83ff8d5df1f..56405224c8a9f4de9cdbf6c7f0aa86a9d79e4d7d 100644 (file)
@@ -189,6 +189,8 @@ rust_take_global_args_lock
 rust_drop_global_args_lock
 rust_take_change_dir_lock
 rust_drop_change_dir_lock
+rust_take_linenoise_lock
+rust_drop_linenoise_lock
 rust_get_test_int
 rust_get_task
 rust_uv_get_loop_from_getaddrinfo_req
diff --git a/src/test/compile-fail/ifmt-bad-format-args.rs b/src/test/compile-fail/ifmt-bad-format-args.rs
new file mode 100644 (file)
index 0000000..d2a3fe2
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    format_args!("test"); //~ ERROR: expected token
+    format_args!("", || {}); //~ ERROR: must be a string literal
+}
diff --git a/src/test/compile-fail/ifmt-bad-format-args2.rs b/src/test/compile-fail/ifmt-bad-format-args2.rs
new file mode 100644 (file)
index 0000000..7bb8365
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    format_args!("{}", ""); //~ ERROR: expected function
+}
index 0b2203be9d7c0d582e4f454370eb1911225381dd..351bad193da2dfc85249039f122df30e7a2241c2 100644 (file)
 #[deny(warnings)];
 
 use std::fmt;
+use std::rt::io::Decorator;
+use std::rt::io::mem::MemWriter;
+use std::rt::io;
+use std::rt::io::Writer;
+use std::str;
 
 struct A;
 struct B;
@@ -236,16 +241,13 @@ pub fn main() {
         let a: int = ::std::cast::transmute(3u);
         format!("{}", a);
     }
+
+    test_format_args();
 }
 
 // Basic test to make sure that we can invoke the `write!` macro with an
 // io::Writer instance.
 fn test_write() {
-    use std::rt::io::Decorator;
-    use std::rt::io::mem::MemWriter;
-    use std::rt::io;
-    use std::str;
-
     let mut buf = MemWriter::new();
     write!(&mut buf as &mut io::Writer, "{}", 3);
     {
@@ -269,3 +271,20 @@ fn test_print() {
     println!("this is a {}", "test");
     println!("{foo}", foo="bar");
 }
+
+// Just make sure that the macros are defined, there's not really a lot that we
+// can do with them just yet (to test the output)
+fn test_format_args() {
+    let mut buf = MemWriter::new();
+    {
+        let w = &mut buf as &mut io::Writer;
+        format_args!(|args| { fmt::write(w, args) }, "{}", 1);
+        format_args!(|args| { fmt::write(w, args) }, "test");
+        format_args!(|args| { fmt::write(w, args) }, "{test}", test=3);
+    }
+    let s = str::from_utf8_owned(buf.inner());
+    t!(s, "1test3");
+
+    let s = format_args!(fmt::format, "hello {}", "world");
+    t!(s, "hello world");
+}
diff --git a/src/test/run-pass/issue-9110.rs b/src/test/run-pass/issue-9110.rs
new file mode 100644 (file)
index 0000000..56f87f5
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! silly_macro(
+    () => (
+        pub mod Qux {
+            pub struct Foo { x : u8 }
+            pub fn bar(_foo : Foo) {}
+        }
+    );
+)
+
+silly_macro!()
+
+fn main() {}
diff --git a/src/test/run-pass/rl-human-test.rs b/src/test/run-pass/rl-human-test.rs
new file mode 100644 (file)
index 0000000..558e0b6
--- /dev/null
@@ -0,0 +1,75 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-fast no compile flags for check-fast
+
+// we want this to be compiled to avoid bitrot, but the actual test
+//has to be conducted by a human, i.e. someone (you?) compiling this
+//file with a plain rustc invocation and running it and checking it
+//works.
+
+// compile-flags: --cfg robot_mode
+
+extern mod extra;
+use extra::rl;
+
+static HISTORY_FILE: &'static str = "rl-human-test-history.txt";
+
+fn main() {
+    // don't run this in robot mode, but still typecheck it.
+    if !cfg!(robot_mode) {
+        println("~~ Welcome to the rl test \"suite\". ~~");
+        println!("Operations:
+ - restrict the history to 2 lines,
+ - set the tab-completion to suggest three copies of each of the last 3 letters (or 'empty'),
+ - add 'one' and 'two' to the history,
+ - save it to `{0}`,
+ - add 'three',
+ - prompt & save input (check the history & completion work and contains only 'two', 'three'),
+ - load from `{0}`
+ - prompt & save input (history should be 'one', 'two' again),
+ - prompt once more.
+
+The bool return values of each step are printed.",
+                 HISTORY_FILE);
+
+        println!("restricting history length: {}", rl::set_history_max_len(3));
+
+        do rl::complete |line, suggest| {
+            if line.is_empty() {
+                suggest(~"empty")
+            } else {
+                for c in line.rev_iter().take(3) {
+                    suggest(format!("{0}{1}{1}{1}", line, c))
+                }
+            }
+        }
+
+        println!("adding 'one': {}", rl::add_history("one"));
+        println!("adding 'two': {}", rl::add_history("two"));
+
+        println!("saving history: {}", rl::save_history(HISTORY_FILE));
+
+        println!("adding 'three': {}", rl::add_history("three"));
+
+        match rl::read("> ") {
+            Some(s) => println!("saving input: {}", rl::add_history(s)),
+            None => return
+        }
+        println!("loading history: {}", rl::load_history(HISTORY_FILE));
+
+        match rl::read("> ") {
+            Some(s) => println!("saving input: {}", rl::add_history(s)),
+            None => return
+        }
+
+        rl::read("> ");
+    }
+}