From 647e54d6d154e1a267e84c8ae9f1315e3f9b93fc Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sat, 27 Dec 2014 23:57:43 +0200 Subject: [PATCH] Fallout of changing format_args!(f, args) to f(format_args!(args)). --- src/libcollections/string.rs | 9 ++ src/libcore/fmt/float.rs | 7 + src/libcore/fmt/mod.rs | 86 +++++++++++ src/libcore/lib.rs | 2 +- src/libcore/macros.rs | 34 +++++ src/libcore/panicking.rs | 43 +++++- src/liblog/lib.rs | 42 +++++- src/liblog/macros.rs | 57 ++++++++ src/libstd/fmt.rs | 61 ++++---- src/libstd/io/mod.rs | 42 ++++++ src/libstd/io/stdio.rs | 20 +++ src/libstd/macros.rs | 136 ++++++++++++++++-- src/libstd/rt/macros.rs | 18 +++ src/libstd/rt/unwind.rs | 49 ++++++- src/libstd/rt/util.rs | 15 +- .../compile-fail/dead-code-closure-bang.rs | 2 +- src/test/compile-fail/ifmt-bad-format-args.rs | 6 +- .../compile-fail/ifmt-bad-format-args2.rs | 13 -- src/test/pretty/issue-4264.pp | 64 +++++---- src/test/run-pass/ifmt.rs | 12 +- 20 files changed, 627 insertions(+), 91 deletions(-) delete mode 100644 src/test/compile-fail/ifmt-bad-format-args2.rs diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 6c2659b13f7..9460b9a8966 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -945,6 +945,15 @@ pub trait ToString { } impl ToString for T { + // NOTE(stage0): Remove cfg after a snapshot + #[cfg(not(stage0))] + fn to_string(&self) -> String { + let mut buf = Vec::::new(); + let _ = fmt::write(&mut buf, format_args!("{}", *self)); + String::from_utf8(buf).unwrap() + } + // NOTE(stage0): Remove method after a snapshot + #[cfg(stage0)] fn to_string(&self) -> String { let mut buf = Vec::::new(); let _ = format_args!(|args| fmt::write(&mut buf, args), "{}", self); diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs index 55a87973e0f..47701ab8ffd 100644 --- a/src/libcore/fmt/float.rs +++ b/src/libcore/fmt/float.rs @@ -325,6 +325,13 @@ pub fn float_to_str_bytes_common( let mut filler = Filler { buf: &mut buf, end: &mut end }; match sign { + // NOTE(stage0): Remove cfg after a snapshot + #[cfg(not(stage0))] + SignNeg => { + let _ = fmt::write(&mut filler, format_args!("{:-}", exp)); + } + // NOTE(stage0): Remove match arm after a snapshot + #[cfg(stage0)] SignNeg => { let _ = format_args!(|args| { fmt::write(&mut filler, args) diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index fb26450ec75..b050b98de2f 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -70,6 +70,16 @@ pub trait FormatWriter { /// This function will return an instance of `FormatError` on error. fn write(&mut self, bytes: &[u8]) -> Result; + // NOTE(stage0): Remove cfg after a snapshot + #[cfg(not(stage0))] + /// Glue for usage of the `write!` macro with implementers of this trait. + /// + /// This method should generally not be invoked manually, but rather through + /// the `write!` macro itself. + fn write_fmt(&mut self, args: Arguments) -> Result { write(self, args) } + + // NOTE(stage0): Remove method after a snapshot + #[cfg(stage0)] /// Glue for usage of the `write!` macro with implementers of this trait. /// /// This method should generally not be invoked manually, but rather through @@ -180,6 +190,7 @@ impl<'a> Arguments<'a> { /// macro validates the format string at compile-time so usage of the `write` /// and `format` functions can be safely performed. #[stable] +#[deriving(Copy)] pub struct Arguments<'a> { // Format string pieces to print. pieces: &'a [&'a str], @@ -193,6 +204,14 @@ pub struct Arguments<'a> { } impl<'a> Show for Arguments<'a> { + // NOTE(stage0): Remove cfg after a snapshot + #[cfg(not(stage0))] + fn fmt(&self, fmt: &mut Formatter) -> Result { + write(fmt.buf, *self) + } + + // NOTE(stage0): Remove method after a snapshot + #[cfg(stage0)] fn fmt(&self, fmt: &mut Formatter) -> Result { write(fmt.buf, self) } @@ -268,6 +287,63 @@ static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument { } }; +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// The `write` 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. +/// +/// # Arguments +/// +/// * output - the buffer to write output to +/// * args - the precompiled arguments generated by `format_args!` +#[experimental = "libcore and I/O have yet to be reconciled, and this is an \ + implementation detail which should not otherwise be exported"] +pub fn write(output: &mut FormatWriter, args: Arguments) -> Result { + let mut formatter = Formatter { + flags: 0, + width: None, + precision: None, + buf: output, + align: rt::AlignUnknown, + fill: ' ', + args: args.args, + curarg: args.args.iter(), + }; + + let mut pieces = args.pieces.iter(); + + match args.fmt { + None => { + // We can use default formatting parameters for all arguments. + for _ in range(0, args.args.len()) { + try!(formatter.buf.write(pieces.next().unwrap().as_bytes())); + try!(formatter.run(&DEFAULT_ARGUMENT)); + } + } + Some(fmt) => { + // Every spec has a corresponding argument that is preceded by + // a string piece. + for (arg, piece) in fmt.iter().zip(pieces.by_ref()) { + try!(formatter.buf.write(piece.as_bytes())); + try!(formatter.run(arg)); + } + } + } + + // There can be only one trailing string piece left. + match pieces.next() { + Some(piece) => { + try!(formatter.buf.write(piece.as_bytes())); + } + None => {} + } + + Ok(()) +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] /// The `write` 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. @@ -527,6 +603,16 @@ impl<'a> Formatter<'a> { self.buf.write(data) } + // NOTE(stage0): Remove cfg after a snapshot + #[cfg(not(stage0))] + /// Writes some formatted information into this instance + #[unstable = "reconciling core and I/O may alter this definition"] + pub fn write_fmt(&mut self, fmt: Arguments) -> Result { + write(self.buf, fmt) + } + + // NOTE(stage0): Remove method after a snapshot + #[cfg(stage0)] /// Writes some formatted information into this instance #[unstable = "reconciling core and I/O may alter this definition"] pub fn write_fmt(&mut self, fmt: &Arguments) -> Result { diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 9de723f38ee..6d0d6e0817a 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -39,7 +39,7 @@ //! distribution. //! //! * `rust_begin_unwind` - This function takes three arguments, a -//! `&fmt::Arguments`, a `&str`, and a `uint`. These three arguments dictate +//! `fmt::Arguments`, a `&str`, and a `uint`. These three arguments dictate //! the panic message, the file at which panic was invoked, and the line. //! It is up to consumers of this core library to define this panic //! function; it is only required to never return. diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 7ce1da7d2d0..2cd9e7c4509 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -10,6 +10,30 @@ #![macro_escape] +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// Entry point of task panic, for details, see std::macros +#[macro_export] +macro_rules! panic { + () => ( + panic!("explicit panic") + ); + ($msg:expr) => ({ + static _MSG_FILE_LINE: (&'static str, &'static str, uint) = ($msg, file!(), line!()); + ::core::panicking::panic(&_MSG_FILE_LINE) + }); + ($fmt:expr, $($arg:tt)*) => ({ + // The leading _'s are to avoid dead code warnings if this is + // used inside a dead function. Just `#[allow(dead_code)]` is + // insufficient, since the user may have + // `#[forbid(dead_code)]` and which cannot be overridden. + static _FILE_LINE: (&'static str, uint) = (file!(), line!()); + ::core::panicking::panic_fmt(format_args!($fmt, $($arg)*), &_FILE_LINE) + }); +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] /// Entry point of task panic, for details, see std::macros #[macro_export] macro_rules! panic { @@ -105,6 +129,16 @@ macro_rules! try { ($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(e) }) } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// Writing a formatted string into a writer +#[macro_export] +macro_rules! write { + ($dst:expr, $($arg:tt)*) => ((&mut *$dst).write_fmt(format_args!($($arg)*))) +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] /// Writing a formatted string into a writer #[macro_export] macro_rules! write { diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index fd0526db411..32f09a4c17f 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -16,7 +16,7 @@ //! interface for panicking is: //! //! ```ignore -//! fn panic_impl(fmt: &fmt::Arguments, &(&'static str, uint)) -> !; +//! fn panic_impl(fmt: fmt::Arguments, &(&'static str, uint)) -> !; //! ``` //! //! This definition allows for panicking with any general message, but it does not @@ -31,8 +31,20 @@ #![allow(dead_code, missing_docs)] use fmt; -use intrinsics; +// NOTE(stage0): Remove import after a snapshot +#[cfg(stage0)] use intrinsics; +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +#[cold] #[inline(never)] // this is the slow path, always +#[lang="panic"] +pub fn panic(expr_file_line: &(&'static str, &'static str, uint)) -> ! { + let (expr, file, line) = *expr_file_line; + panic_fmt(format_args!("{}", expr), &(file, line)) +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] #[cold] #[inline(never)] // this is the slow path, always #[lang="panic"] pub fn panic(expr_file_line: &(&'static str, &'static str, uint)) -> ! { @@ -45,6 +57,18 @@ pub fn panic(expr_file_line: &(&'static str, &'static str, uint)) -> ! { unsafe { intrinsics::abort() } } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +#[cold] #[inline(never)] +#[lang="panic_bounds_check"] +fn panic_bounds_check(file_line: &(&'static str, uint), + index: uint, len: uint) -> ! { + panic_fmt(format_args!("index out of bounds: the len is {} but the index is {}", + len, index), file_line) +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] #[cold] #[inline(never)] #[lang="panic_bounds_check"] fn panic_bounds_check(file_line: &(&'static str, uint), @@ -55,6 +79,21 @@ fn panic_bounds_check(file_line: &(&'static str, uint), unsafe { intrinsics::abort() } } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +#[cold] #[inline(never)] +pub fn panic_fmt(fmt: fmt::Arguments, file_line: &(&'static str, uint)) -> ! { + #[allow(improper_ctypes)] + extern { + #[lang = "panic_fmt"] + fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: uint) -> !; + } + let (file, line) = *file_line; + unsafe { panic_impl(fmt, file, line) } +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] #[cold] #[inline(never)] pub fn panic_fmt(fmt: &fmt::Arguments, file_line: &(&'static str, uint)) -> ! { #[allow(improper_ctypes)] diff --git a/src/liblog/lib.rs b/src/liblog/lib.rs index 97301628a45..1d865868f18 100644 --- a/src/liblog/lib.rs +++ b/src/liblog/lib.rs @@ -268,6 +268,8 @@ impl Drop for DefaultLogger { } } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] /// This function is called directly by the compiler when using the logging /// macros. This function does not take into account whether the log level /// specified is active or not, it will always log something if this method is @@ -276,7 +278,7 @@ impl Drop for DefaultLogger { /// It is not recommended to call this function directly, rather it should be /// invoked through the logging family of macros. #[doc(hidden)] -pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) { +pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) { // Test the literal string from args against the current filter, if there // is one. match unsafe { FILTER.as_ref() } { @@ -302,6 +304,42 @@ pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) { set_logger(logger); } +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] +/// This function is called directly by the compiler when using the logging +/// macros. This function does not take into account whether the log level +/// specified is active or not, it will always log something if this method is +/// called. +/// +/// It is not recommended to call this function directly, rather it should be +/// invoked through the logging family of macros. +#[doc(hidden)] +pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) { + // Test the literal string from args against the current filter, if there + // is one. + match unsafe { FILTER.as_ref() } { + Some(filter) if !filter.is_match(args.to_string().as_slice()) => return, + _ => {} + } + + // Completely remove the local logger from TLS in case anyone attempts to + // frob the slot while we're doing the logging. This will destroy any logger + // set during logging. + let mut logger = LOCAL_LOGGER.with(|s| { + s.borrow_mut().take() + }).unwrap_or_else(|| { + box DefaultLogger { handle: io::stderr() } as Box + }); + logger.log(&LogRecord { + level: LogLevel(level), + args: *args, + file: loc.file, + module_path: loc.module_path, + line: loc.line, + }); + set_logger(logger); +} + /// Getter for the global log level. This is a function so that it can be called /// safely #[doc(hidden)] @@ -329,7 +367,7 @@ pub struct LogRecord<'a> { pub level: LogLevel, /// The arguments from the log line. - pub args: &'a fmt::Arguments<'a>, + pub args: fmt::Arguments<'a>, /// The file of where the LogRecord originated. pub file: &'a str, diff --git a/src/liblog/macros.rs b/src/liblog/macros.rs index 8b2cfcd420a..2e8302cc10f 100644 --- a/src/liblog/macros.rs +++ b/src/liblog/macros.rs @@ -12,6 +12,63 @@ #![macro_escape] +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// The standard logging macro +/// +/// This macro will generically log over a provided level (of type u32) with a +/// format!-based argument list. See documentation in `std::fmt` for details on +/// how to use the syntax. +/// +/// # Example +/// +/// ``` +/// #![feature(phase)] +/// #[phase(plugin, link)] extern crate log; +/// +/// fn main() { +/// log!(log::WARN, "this is a warning {}", "message"); +/// log!(log::DEBUG, "this is a debug message"); +/// log!(6, "this is a custom logging level: {level}", level=6u); +/// } +/// ``` +/// +/// Assumes the binary is `main`: +/// +/// ```{.bash} +/// $ RUST_LOG=warn ./main +/// WARN:main: this is a warning message +/// ``` +/// +/// ```{.bash} +/// $ RUST_LOG=debug ./main +/// DEBUG:main: this is a debug message +/// WARN:main: this is a warning message +/// ``` +/// +/// ```{.bash} +/// $ RUST_LOG=6 ./main +/// DEBUG:main: this is a debug message +/// WARN:main: this is a warning message +/// 6:main: this is a custom logging level: 6 +/// ``` +#[macro_export] +macro_rules! log { + ($lvl:expr, $($arg:tt)+) => ({ + static LOC: ::log::LogLocation = ::log::LogLocation { + line: line!(), + file: file!(), + module_path: module_path!(), + }; + let lvl = $lvl; + if log_enabled!(lvl) { + ::log::log(lvl, &LOC, format_args!($($arg)+)) + } + }) +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] /// The standard logging macro /// /// This macro will generically log over a provided level (of type u32) with a diff --git a/src/libstd/fmt.rs b/src/libstd/fmt.rs index d0c9df9d914..b75cf9a196b 100644 --- a/src/libstd/fmt.rs +++ b/src/libstd/fmt.rs @@ -16,13 +16,6 @@ //! This macro is implemented in the compiler to emit calls to this module in //! order to format arguments at runtime into strings and streams. //! -//! The functions contained in this module should not normally be used in -//! everyday use cases of `format!`. The assumptions made by these functions are -//! unsafe for all inputs, and the compiler performs a large amount of -//! validation on the arguments to `format!` in order to ensure safety at -//! runtime. While it is possible to call these functions directly, it is not -//! recommended to do so in the general case. -//! //! ## Usage //! //! The `format!` macro is intended to be familiar to those coming from C's @@ -275,35 +268,28 @@ //! //! # #[allow(unused_must_use)] //! # fn main() { -//! format_args!(fmt::format, "this returns {}", "String"); +//! fmt::format(format_args!("this returns {}", "String")); //! //! let some_writer: &mut io::Writer = &mut io::stdout(); -//! format_args!(|args| { write!(some_writer, "{}", args) }, -//! "print with a {}", "closure"); +//! write!(some_writer, "{}", format_args!("print with a {}", "macro")); //! -//! fn my_fmt_fn(args: &fmt::Arguments) { +//! fn my_fmt_fn(args: fmt::Arguments) { //! write!(&mut io::stdout(), "{}", args); //! } -//! format_args!(my_fmt_fn, "or a {} too", "function"); +//! my_fmt_fn(format_args!("or a {} too", "function")); //! # } //! ``` //! -//! The first argument of the `format_args!` macro is a function (or closure) -//! which takes one argument of type `&fmt::Arguments`. This structure can then -//! be passed to the `write` and `format` functions inside this module in order -//! to process the format string. The goal of this macro is to even further -//! prevent intermediate allocations when dealing formatting strings. +//! The result of the `format_args!` macro is a value of type `fmt::Arguments`. +//! This structure can then be passed to the `write` and `format` functions +//! inside this module in order to process the format string. +//! The goal of this macro is to even further prevent intermediate allocations +//! when dealing formatting strings. //! //! For example, a logging library could use the standard formatting syntax, but //! it would internally pass around this structure until it has been determined //! where output should go to. //! -//! It is unsafe to programmatically create an instance of `fmt::Arguments` -//! because the operations performed when executing a format string require the -//! compile-time checks provided by the compiler. The `format_args!` macro is -//! the only method of safely creating these structures, but they can be -//! unsafely created with the constructor provided. -//! //! ## Syntax //! //! The syntax for the formatting language used is drawn from other languages, @@ -420,14 +406,39 @@ pub use core::fmt::{Argument, Arguments, write, radix, Radix, RadixFmt}; #[doc(hidden)] pub use core::fmt::{argument, argumentuint}; +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// 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. +/// +/// # Example +/// +/// ```rust +/// use std::fmt; +/// +/// let s = fmt::format(format_args!("Hello, {}!", "world")); +/// assert_eq!(s, "Hello, world!".to_string()); +/// ``` +#[experimental = "this is an implementation detail of format! and should not \ + be called directly"] +pub fn format(args: Arguments) -> string::String { + let mut output = Vec::new(); + let _ = write!(&mut output as &mut Writer, "{}", args); + string::String::from_utf8(output).unwrap() +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] /// 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 /// diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 233ad781093..b6f8bb25b65 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1017,6 +1017,48 @@ pub trait Writer { /// decide whether their stream needs to be buffered or not. fn flush(&mut self) -> IoResult<()> { Ok(()) } + // NOTE(stage0): Remove cfg after a snapshot + #[cfg(not(stage0))] + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the `format_args!` + /// macro, but it is rare that this should explicitly be called. The + /// `write!` macro should be favored to invoke this method instead. + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + fn write_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> { + // Create a shim which translates a Writer to a FormatWriter and saves + // off I/O errors. instead of discarding them + struct Adaptor<'a, T:'a> { + inner: &'a mut T, + error: IoResult<()>, + } + + impl<'a, T: Writer> fmt::FormatWriter for Adaptor<'a, T> { + fn write(&mut self, bytes: &[u8]) -> fmt::Result { + match self.inner.write(bytes) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adaptor { inner: self, error: Ok(()) }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => output.error + } + } + + + // NOTE(stage0): Remove method after a snapshot + #[cfg(stage0)] /// Writes a formatted string into this writer, returning any error /// encountered. /// diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index b7da57fed27..6bd721599f3 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -378,12 +378,32 @@ pub fn println(s: &str) { }) } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible +/// with the `format_args!` macro. +pub fn print_args(fmt: fmt::Arguments) { + with_task_stdout(|io| write!(io, "{}", fmt)) +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] /// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible /// with the `format_args!` macro. pub fn print_args(fmt: &fmt::Arguments) { with_task_stdout(|io| write!(io, "{}", fmt)) } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// Similar to `println`, but takes a `fmt::Arguments` structure to be +/// compatible with the `format_args!` macro. +pub fn println_args(fmt: fmt::Arguments) { + with_task_stdout(|io| writeln!(io, "{}", fmt)) +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] /// Similar to `println`, but takes a `fmt::Arguments` structure to be /// compatible with the `format_args!` macro. pub fn println_args(fmt: &fmt::Arguments) { diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index d90a47ea4ea..edb6218c5cc 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -17,6 +17,50 @@ #![experimental] #![macro_escape] +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// The entry point for panic of Rust tasks. +/// +/// This macro is used to inject panic into a Rust task, causing the task to +/// unwind and panic entirely. Each task's panic can be reaped as the +/// `Box` type, and the single-argument form of the `panic!` macro will be +/// the value which is transmitted. +/// +/// The multi-argument form of this macro panics with a string and has the +/// `format!` syntax for building a string. +/// +/// # Example +/// +/// ```should_fail +/// # #![allow(unreachable_code)] +/// panic!(); +/// panic!("this is a terrible mistake!"); +/// panic!(4i); // panic with the value of 4 to be collected elsewhere +/// panic!("this is a {} {message}", "fancy", message = "message"); +/// ``` +#[macro_export] +macro_rules! panic { + () => ({ + panic!("explicit panic") + }); + ($msg:expr) => ({ + // static requires less code at runtime, more constant data + static _FILE_LINE: (&'static str, uint) = (file!(), line!()); + ::std::rt::begin_unwind($msg, &_FILE_LINE) + }); + ($fmt:expr, $($arg:tt)*) => ({ + // The leading _'s are to avoid dead code warnings if this is + // used inside a dead function. Just `#[allow(dead_code)]` is + // insufficient, since the user may have + // `#[forbid(dead_code)]` and which cannot be overridden. + static _FILE_LINE: (&'static str, uint) = (file!(), line!()); + ::std::rt::begin_unwind_fmt(format_args!($fmt, $($arg)*), &_FILE_LINE) + + }); +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] /// The entry point for panic of Rust tasks. /// /// This macro is used to inject panic into a Rust task, causing the task to @@ -245,6 +289,26 @@ macro_rules! unimplemented { () => (panic!("not yet implemented")) } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// Use the syntax described in `std::fmt` to create a value of type `String`. +/// See `std::fmt` for more information. +/// +/// # Example +/// +/// ``` +/// format!("test"); +/// format!("hello {}", "world!"); +/// format!("x = {}, y = {y}", 10i, y = 30i); +/// ``` +#[macro_export] +#[stable] +macro_rules! format { + ($($arg:tt)*) => (::std::fmt::format(format_args!($($arg)*))) +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] /// Use the syntax described in `std::fmt` to create a value of type `String`. /// See `std::fmt` for more information. /// @@ -263,6 +327,28 @@ macro_rules! format { ) } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// Use the `format!` syntax to write data into a buffer of type `&mut Writer`. +/// See `std::fmt` for more information. +/// +/// # Example +/// +/// ``` +/// # #![allow(unused_must_use)] +/// +/// let mut w = Vec::new(); +/// write!(&mut w, "test"); +/// write!(&mut w, "formatted {}", "arguments"); +/// ``` +#[macro_export] +#[stable] +macro_rules! write { + ($dst:expr, $($arg:tt)*) => ((&mut *$dst).write_fmt(format_args!($($arg)*))) +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] /// Use the `format!` syntax to write data into a buffer of type `&mut Writer`. /// See `std::fmt` for more information. /// @@ -294,6 +380,18 @@ macro_rules! writeln { ) } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// Equivalent to the `println!` macro except that a newline is not printed at +/// the end of the message. +#[macro_export] +#[stable] +macro_rules! print { + ($($arg:tt)*) => (::std::io::stdio::print_args(format_args!($($arg)*))) +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] /// Equivalent to the `println!` macro except that a newline is not printed at /// the end of the message. #[macro_export] @@ -302,6 +400,28 @@ macro_rules! print { ($($arg:tt)*) => (format_args!(::std::io::stdio::print_args, $($arg)*)) } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// Macro for printing to a task's stdout handle. +/// +/// Each task can override its stdout handle via `std::io::stdio::set_stdout`. +/// The syntax of this macro is the same as that used for `format!`. For more +/// information, see `std::fmt` and `std::io::stdio`. +/// +/// # Example +/// +/// ``` +/// println!("hello there!"); +/// println!("format {} arguments", "some"); +/// ``` +#[macro_export] +#[stable] +macro_rules! println { + ($($arg:tt)*) => (::std::io::stdio::println_args(format_args!($($arg)*))) +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] /// Macro for printing to a task's stdout handle. /// /// Each task can override its stdout handle via `std::io::stdio::set_stdout`. @@ -411,11 +531,10 @@ macro_rules! log { pub mod builtin { /// The core macro for formatted string creation & output. /// - /// This macro takes as its first argument a callable expression which will - /// receive as its first argument a value of type `&fmt::Arguments`. This - /// value can be passed to the functions in `std::fmt` for performing useful - /// functions. All other formatting macros (`format!`, `write!`, - /// `println!`, etc) are proxied through this one. + /// This macro produces a value of type `fmt::Arguments`. This value can be + /// passed to the functions in `std::fmt` for performing useful functions. + /// All other formatting macros (`format!`, `write!`, `println!`, etc) are + /// proxied through this one. /// /// For more information, see the documentation in `std::fmt`. /// @@ -424,15 +543,12 @@ pub mod builtin { /// ```rust /// use std::fmt; /// - /// let s = format_args!(fmt::format, "hello {}", "world"); + /// let s = fmt::format(format_args!("hello {}", "world")); /// assert_eq!(s, format!("hello {}", "world")); /// - /// format_args!(|args| { - /// // pass `args` to another function, etc. - /// }, "hello {}", "world"); /// ``` #[macro_export] - macro_rules! format_args { ($closure:expr, $fmt:expr $($args:tt)*) => ({ + macro_rules! format_args { ($fmt:expr $($args:tt)*) => ({ /* compiler built-in */ }) } diff --git a/src/libstd/rt/macros.rs b/src/libstd/rt/macros.rs index bee8b5b82f4..095a27203f9 100644 --- a/src/libstd/rt/macros.rs +++ b/src/libstd/rt/macros.rs @@ -15,6 +15,16 @@ #![macro_escape] +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +macro_rules! rterrln { + ($fmt:expr $($arg:tt)*) => ( { + ::rt::util::dumb_print(format_args!(concat!($fmt, "\n") $($arg)*)) + } ) +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] macro_rules! rterrln { ($fmt:expr $($arg:tt)*) => ( { format_args!(::rt::util::dumb_print, concat!($fmt, "\n") $($arg)*) @@ -40,6 +50,14 @@ macro_rules! rtassert { } ) } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +macro_rules! rtabort { + ($($arg:tt)*) => (::rt::util::abort(format_args!($($arg)*))) +} + +// NOTE(stage0): Remove macro after a snapshot +#[cfg(stage0)] macro_rules! rtabort { ($($arg:tt)*) => (format_args!(::rt::util::abort, $($arg)*)) } diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index eb15a7ba378..261a8335173 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -477,14 +477,61 @@ pub mod eabi { } } -// Entry point of panic from the libcore crate +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] #[cfg(not(test))] +/// Entry point of panic from the libcore crate. +#[lang = "panic_fmt"] +pub extern fn rust_begin_unwind(msg: fmt::Arguments, + file: &'static str, line: uint) -> ! { + begin_unwind_fmt(msg, &(file, line)) +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] +#[cfg(not(test))] +/// Entry point of panic from the libcore crate. #[lang = "panic_fmt"] pub extern fn rust_begin_unwind(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! { begin_unwind_fmt(msg, &(file, line)) } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +/// The entry point for unwinding with a formatted message. +/// +/// This is designed to reduce the amount of code required at the call +/// site as much as possible (so that `panic!()` has as low an impact +/// on (e.g.) the inlining of other functions as possible), by moving +/// the actual formatting into this shared place. +#[inline(never)] #[cold] +pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, uint)) -> ! { + use fmt::FormatWriter; + + // We do two allocations here, unfortunately. But (a) they're + // required with the current scheme, and (b) we don't handle + // panic + OOM properly anyway (see comment in begin_unwind + // below). + + struct VecWriter<'a> { v: &'a mut Vec } + + impl<'a> fmt::FormatWriter for VecWriter<'a> { + fn write(&mut self, buf: &[u8]) -> fmt::Result { + self.v.push_all(buf); + Ok(()) + } + } + + let mut v = Vec::new(); + let _ = write!(&mut VecWriter { v: &mut v }, "{}", msg); + + let msg = box String::from_utf8_lossy(v.as_slice()).into_owned(); + begin_unwind_inner(msg, file_line) +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] /// The entry point for unwinding with a formatted message. /// /// This is designed to reduce the amount of code required at the call diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index d8cd8455deb..26dadfd9fb1 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -112,12 +112,25 @@ impl fmt::FormatWriter for Stdio { } } +// NOTE(stage0): Remove cfg after a snapshot +#[cfg(not(stage0))] +pub fn dumb_print(args: fmt::Arguments) { + let _ = Stderr.write_fmt(args); +} + +// NOTE(stage0): Remove function after a snapshot +#[cfg(stage0)] pub fn dumb_print(args: &fmt::Arguments) { let mut w = Stderr; let _ = write!(&mut w, "{}", args); } -pub fn abort(args: &fmt::Arguments) -> ! { +// NOTE(stage0): Remove wrappers after a snapshot +#[cfg(not(stage0))] pub fn abort(args: fmt::Arguments) -> ! { abort_(&args) } +#[cfg(stage0)] pub fn abort(args: &fmt::Arguments) -> ! { abort_(args) } + +// NOTE(stage0): Change to `pub fn abort(args: fmt::Arguments) -> !` after a snapshot +fn abort_(args: &fmt::Arguments) -> ! { use fmt::FormatWriter; struct BufWriter<'a> { diff --git a/src/test/compile-fail/dead-code-closure-bang.rs b/src/test/compile-fail/dead-code-closure-bang.rs index 3179b181560..0aa3c40fa5f 100644 --- a/src/test/compile-fail/dead-code-closure-bang.rs +++ b/src/test/compile-fail/dead-code-closure-bang.rs @@ -13,5 +13,5 @@ fn main() { let x: || -> ! = || panic!(); x(); - println!("Foo bar"); //~ ERROR: unreachable statement + std::io::println("Foo bar"); //~ ERROR: unreachable statement } diff --git a/src/test/compile-fail/ifmt-bad-format-args.rs b/src/test/compile-fail/ifmt-bad-format-args.rs index d2a3fe2a9b1..8bd3cb0f60b 100644 --- a/src/test/compile-fail/ifmt-bad-format-args.rs +++ b/src/test/compile-fail/ifmt-bad-format-args.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -9,6 +9,6 @@ // except according to those terms. fn main() { - format_args!("test"); //~ ERROR: expected token - format_args!("", || {}); //~ ERROR: must be a string literal + format_args!(); //~ ERROR: requires at least a format string argument + 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 deleted file mode 100644 index 7bb8365bc12..00000000000 --- a/src/test/compile-fail/ifmt-bad-format-args2.rs +++ /dev/null @@ -1,13 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn main() { - format_args!("{}", ""); //~ ERROR: expected function -} diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index e4389cd69dd..c2ebd764ad6 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -34,13 +34,6 @@ pub fn bar() { [int; 3]) as &[int; 3]) as *const _ as *const [int; 3]) as *const [int; (3u as uint)] as *const [int; 3]); - (match (() as ()) { - () => { - #[inline] - #[allow(dead_code)] - static __STATIC_FMTSTR: &'static [&'static str] = - (&([("test" as &'static str)] as [&'static str; 1]) as - &'static [&'static str; 1]); @@ -49,24 +42,45 @@ pub fn bar() { - ((::std::fmt::format as - fn(&core::fmt::Arguments<'_>) -> collections::string::String {std::fmt::format})((&((::std::fmt::Arguments::new - as - fn(&[&str], &[core::fmt::Argument<'_>]) -> core::fmt::Arguments<'_> {core::fmt::Arguments<'a>::new})((__STATIC_FMTSTR - as - &'static [&'static str]), - (&([] - as - [core::fmt::Argument<'_>; 0]) - as - &[core::fmt::Argument<'_>; 0])) - as - core::fmt::Arguments<'_>) - as - &core::fmt::Arguments<'_>)) - as collections::string::String) - } - } as collections::string::String); + ((::std::fmt::format as + fn(core::fmt::Arguments<'_>) -> collections::string::String {std::fmt::format})(((::std::fmt::Arguments::new + as + fn(&[&str], &[core::fmt::Argument<'_>]) -> core::fmt::Arguments<'_> {core::fmt::Arguments<'a>::new})(({ + #[inline] + #[allow(dead_code)] + static __STATIC_FMTSTR: + &'static [&'static str] + = + (&([("test" + as + &'static str)] + as + [&'static str; 1]) + as + &'static [&'static str; 1]); + (__STATIC_FMTSTR + as + &'static [&'static str]) + } + as + &[&str]), + (&(match (() + as + ()) + { + () + => + ([] + as + [core::fmt::Argument<'_>; 0]), + } + as + [core::fmt::Argument<'_>; 0]) + as + &[core::fmt::Argument<'_>; 0])) + as + core::fmt::Arguments<'_>)) + as collections::string::String); } pub type Foo = [int; (3u as uint)]; pub struct Bar { diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index f3e15562b6d..fa62699a303 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -190,18 +190,16 @@ fn test_format_args() { let mut buf = Vec::new(); { let w = &mut buf as &mut io::Writer; - format_args!(|args| { write!(w, "{}", args); }, "{}", 1i); - format_args!(|args| { write!(w, "{}", args); }, "test"); - format_args!(|args| { write!(w, "{}", args); }, "{test}", test=3i); + write!(w, "{}", format_args!("{}", 1i)); + write!(w, "{}", format_args!("test")); + write!(w, "{}", format_args!("{test}", test=3i)); } let s = String::from_utf8(buf).unwrap(); t!(s, "1test3"); - let s = format_args!(fmt::format, "hello {}", "world"); + let s = fmt::format(format_args!("hello {}", "world")); t!(s, "hello world"); - let s = format_args!(|args| { - format!("{}: {}", "args were", args) - }, "hello {}", "world"); + let s = format!("{}: {}", "args were", format_args!("hello {}", "world")); t!(s, "args were: hello world"); }