diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 7c4494358b1..9f64e1aa492 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -113,6 +113,19 @@ impl<'a> Arguments<'a> { /// 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. + #[cfg(not(stage0))] + #[doc(hidden)] #[inline] + pub unsafe fn new<'a>(pieces: &'static [&'static str], + fmt: &'static [rt::Argument<'static>], + args: &'a [Argument<'a>]) -> Arguments<'a> { + Arguments { + pieces: mem::transmute(pieces), + fmt: mem::transmute(fmt), + args: args + } + } + + #[cfg(stage0)] #[doc(hidden)] #[inline] pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>], args: &'a [Argument<'a>]) -> Arguments<'a> { @@ -129,6 +142,14 @@ impl<'a> Arguments<'a> { /// and pass it to a function or closure, passed as the first argument. The /// macro validates the format string at compile-time so usage of the `write` /// and `format` functions can be safely performed. +#[cfg(not(stage0))] +pub struct Arguments<'a> { + pieces: &'a [&'a str], + fmt: &'a [rt::Argument<'a>], + args: &'a [Argument<'a>], +} + +#[cfg(stage0)] #[doc(hidden)] pub struct Arguments<'a> { fmt: &'a [rt::Piece<'a>], args: &'a [Argument<'a>], @@ -263,6 +284,37 @@ uniform_fn_call_workaround! { /// /// * output - the buffer to write output to /// * args - the precompiled arguments generated by `format_args!` +#[cfg(not(stage0))] +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(); + + for arg in args.fmt.iter() { + try!(formatter.buf.write(pieces.next().unwrap().as_bytes())); + try!(formatter.run(arg)); + } + + match pieces.next() { + Some(piece) => { + try!(formatter.buf.write(piece.as_bytes())); + } + None => {} + } + + Ok(()) +} + +#[cfg(stage0)] #[doc(hidden)] pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result { let mut formatter = Formatter { flags: 0, @@ -285,7 +337,26 @@ impl<'a> Formatter<'a> { // First up is the collection of functions used to execute a format string // at runtime. This consumes all of the compile-time statics generated by // the format! syntax extension. + #[cfg(not(stage0))] + fn run(&mut self, arg: &rt::Argument) -> Result { + // Fill in the format parameters into the formatter + self.fill = arg.format.fill; + self.align = arg.format.align; + self.flags = arg.format.flags; + self.width = self.getcount(&arg.format.width); + self.precision = self.getcount(&arg.format.precision); + // Extract the correct argument + let value = match arg.position { + rt::ArgumentNext => { *self.curarg.next().unwrap() } + rt::ArgumentIs(i) => self.args[i], + }; + + // Then actually do some printing + (value.formatter)(value.value, self) + } + + #[cfg(stage0)] #[doc(hidden)] fn run(&mut self, piece: &rt::Piece) -> Result { match *piece { rt::String(s) => self.buf.write(s.as_bytes()), diff --git a/src/libcore/fmt/rt.rs b/src/libcore/fmt/rt.rs index 1f5449130ec..59fbde88d8b 100644 --- a/src/libcore/fmt/rt.rs +++ b/src/libcore/fmt/rt.rs @@ -14,7 +14,7 @@ //! These definitions are similar to their `ct` equivalents, but differ in that //! these can be statically allocated and are slightly optimized for the runtime - +#[cfg(stage0)] #[doc(hidden)] pub enum Piece<'a> { String(&'a str), diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index b3bb01e1d04..fc4d8c83c51 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -49,11 +49,13 @@ struct Context<'a, 'b:'a> { name_types: HashMap, name_ordering: Vec, - /// The latest consecutive literal strings - literal: Option, + /// The latest consecutive literal strings, or empty if there weren't any. + literal: String, - /// Collection of the compiled `rt::Piece` structures + /// Collection of the compiled `rt::Argument` structures pieces: Vec>, + /// Collection of string literals + str_pieces: Vec>, name_positions: HashMap, method_statics: Vec>, @@ -370,28 +372,22 @@ impl<'a, 'b> Context<'a, 'b> { } } - /// Translate the accumulated string literals to a static `rt::Piece` - fn trans_literal_string(&mut self) -> Option> { + /// Translate the accumulated string literals to a literal expression + fn trans_literal_string(&mut self) -> Gc { let sp = self.fmtsp; - self.literal.take().map(|s| { - let s = token::intern_and_get_ident(s.as_slice()); - self.ecx.expr_call_global(sp, - self.rtpath("String"), - vec!( - self.ecx.expr_str(sp, s) - )) - }) + let s = token::intern_and_get_ident(self.literal.as_slice()); + self.literal.clear(); + self.ecx.expr_str(sp, s) } - /// Translate a `parse::Piece` to a static `rt::Piece` + /// Translate a `parse::Piece` to a static `rt::Argument` or append + /// to the `literal` string. fn trans_piece(&mut self, piece: &parse::Piece) -> Option> { + // let mut is_not_default = true; let sp = self.fmtsp; match *piece { parse::String(s) => { - match self.literal { - Some(ref mut sb) => sb.push_str(s), - ref mut empty => *empty = Some(String::from_str(s)), - } + self.literal.push_str(s); None } parse::Argument(ref arg) => { @@ -450,10 +446,9 @@ impl<'a, 'b> Context<'a, 'b> { self.ecx.field_imm(sp, self.ecx.ident_of("width"), width))); let path = self.ecx.path_global(sp, self.rtpath("Argument")); - let s = self.ecx.expr_struct(sp, path, vec!( + Some(self.ecx.expr_struct(sp, path, vec!( self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos), - self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))); - Some(self.ecx.expr_call_global(sp, self.rtpath("Argument"), vec!(s))) + self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt)))) } } } @@ -476,6 +471,30 @@ impl<'a, 'b> Context<'a, 'b> { // Next, build up the static array which will become our precompiled // format "string" + let fmt = self.ecx.expr_vec(self.fmtsp, self.str_pieces.clone()); + let piece_ty = self.ecx.ty_rptr(self.fmtsp, + self.ecx.ty_ident(self.fmtsp, + self.ecx.ident_of("str")), + Some(self.ecx.lifetime(self.fmtsp, + self.ecx.ident_of( + "'static").name)), + ast::MutImmutable); + + let ty = ast::TyFixedLengthVec( + piece_ty, + self.ecx.expr_uint(self.fmtsp, self.str_pieces.len()) + ); + let ty = self.ecx.ty(self.fmtsp, ty); + let st = ast::ItemStatic(ty, ast::MutImmutable, fmt); + let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR"); + let item = self.ecx.item(self.fmtsp, static_str_name, + self.static_attrs(), st); + let decl = respan(self.fmtsp, ast::DeclItem(item)); + lets.push(box(GC) respan(self.fmtsp, + ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))); + + // Then, 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, @@ -483,7 +502,7 @@ impl<'a, 'b> Context<'a, 'b> { self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), self.ecx.ident_of("rt"), - self.ecx.ident_of("Piece")), + self.ecx.ident_of("Argument")), vec!(self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name)), Vec::new() @@ -494,8 +513,8 @@ impl<'a, 'b> Context<'a, 'b> { ); let ty = self.ecx.ty(self.fmtsp, ty); let st = ast::ItemStatic(ty, ast::MutImmutable, fmt); - let static_name = self.ecx.ident_of("__STATIC_FMTSTR"); - let item = self.ecx.item(self.fmtsp, static_name, + let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS"); + let item = self.ecx.item(self.fmtsp, static_args_name, self.static_attrs(), st); let decl = respan(self.fmtsp, ast::DeclItem(item)); lets.push(box(GC) respan(self.fmtsp, @@ -545,13 +564,14 @@ impl<'a, 'b> Context<'a, 'b> { } // Now create the fmt::Arguments struct with all our locals we created. - let fmt = self.ecx.expr_ident(self.fmtsp, static_name); + let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name); + let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name); let args_slice = self.ecx.expr_ident(self.fmtsp, slicename); let result = self.ecx.expr_call_global(self.fmtsp, vec!( self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), self.ecx.ident_of("Arguments"), - self.ecx.ident_of("new")), vec!(fmt, args_slice)); + self.ecx.ident_of("new")), vec!(pieces, fmt, args_slice)); // We did all the work of making sure that the arguments // structure is safe, so we can safely have an unsafe block. @@ -718,8 +738,9 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, name_ordering: name_ordering, nest_level: 0, next_arg: 0, - literal: None, + literal: String::new(), pieces: Vec::new(), + str_pieces: Vec::new(), method_statics: Vec::new(), fmtsp: sp, }; @@ -739,8 +760,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, cx.verify_piece(&piece); match cx.trans_piece(&piece) { Some(piece) => { - cx.trans_literal_string().map(|piece| - cx.pieces.push(piece)); + let s = cx.trans_literal_string(); + cx.str_pieces.push(s); cx.pieces.push(piece); } None => {} @@ -758,7 +779,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, } None => {} } - cx.trans_literal_string().map(|piece| cx.pieces.push(piece)); + if !cx.literal.is_empty() { + let s = cx.trans_literal_string(); + cx.str_pieces.push(s); + } // Make sure that all arguments were used and all arguments have types. for (i, ty) in cx.arg_types.iter().enumerate() { diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index 86e394e5408..376a3a38fda 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -39,26 +39,20 @@ pub fn bar() { () => { #[inline] #[allow(dead_code)] - static __STATIC_FMTSTR: - [::std::fmt::rt::Piece<'static>, ..(1u as uint)] = - ([((::std::fmt::rt::String as - fn(&'static str) -> core::fmt::rt::Piece<'static>)(("test" - as - &'static str)) - as core::fmt::rt::Piece<'static>)] as - [core::fmt::rt::Piece<'static>, .. 1]); + static __STATIC_FMTSTR: [&'static str, ..(1u as uint)] = + ([("test" as &'static str)] as [&'static str, .. 1]); let __args_vec = (&([] as [core::fmt::Argument<'_>, .. 0]) as &[core::fmt::Argument<'_>, .. 0]); let __args = (unsafe { ((::std::fmt::Arguments::new as - unsafe fn(&'static [core::fmt::rt::Piece<'static>], &'a [core::fmt::Argument<'a>]) -> core::fmt::Arguments<'a>)((__STATIC_FMTSTR - as - [core::fmt::rt::Piece<'static>, .. 1]), - (__args_vec - as - &[core::fmt::Argument<'_>, .. 0])) + unsafe fn(&'static [&'static str], &'a [core::fmt::Argument<'a>]) -> core::fmt::Arguments<'a>)((__STATIC_FMTSTR + as + [&'static str, .. 1]), + (__args_vec + as + &[core::fmt::Argument<'_>, .. 0])) as core::fmt::Arguments<'_>) } as core::fmt::Arguments<'_>);