Skip to content

Commit 696367f

Browse files
committed
Decouple string and argument pieces
1 parent 6f34760 commit 696367f

File tree

4 files changed

+134
-45
lines changed

4 files changed

+134
-45
lines changed

src/libcore/fmt/mod.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,19 @@ impl<'a> Arguments<'a> {
113113
/// Arguments structure. The compiler inserts an `unsafe` block to call this,
114114
/// which is valid because the compiler performs all necessary validation to
115115
/// ensure that the resulting call to format/write would be safe.
116+
#[cfg(not(stage0))]
117+
#[doc(hidden)] #[inline]
118+
pub unsafe fn new<'a>(pieces: &'static [&'static str],
119+
fmt: &'static [rt::Argument<'static>],
120+
args: &'a [Argument<'a>]) -> Arguments<'a> {
121+
Arguments {
122+
pieces: mem::transmute(pieces),
123+
fmt: mem::transmute(fmt),
124+
args: args
125+
}
126+
}
127+
128+
#[cfg(stage0)]
116129
#[doc(hidden)] #[inline]
117130
pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
118131
args: &'a [Argument<'a>]) -> Arguments<'a> {
@@ -129,6 +142,14 @@ impl<'a> Arguments<'a> {
129142
/// and pass it to a function or closure, passed as the first argument. The
130143
/// macro validates the format string at compile-time so usage of the `write`
131144
/// and `format` functions can be safely performed.
145+
#[cfg(not(stage0))]
146+
pub struct Arguments<'a> {
147+
pieces: &'a [&'a str],
148+
fmt: &'a [rt::Argument<'a>],
149+
args: &'a [Argument<'a>],
150+
}
151+
152+
#[cfg(stage0)] #[doc(hidden)]
132153
pub struct Arguments<'a> {
133154
fmt: &'a [rt::Piece<'a>],
134155
args: &'a [Argument<'a>],
@@ -263,6 +284,37 @@ uniform_fn_call_workaround! {
263284
///
264285
/// * output - the buffer to write output to
265286
/// * args - the precompiled arguments generated by `format_args!`
287+
#[cfg(not(stage0))]
288+
pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
289+
let mut formatter = Formatter {
290+
flags: 0,
291+
width: None,
292+
precision: None,
293+
buf: output,
294+
align: rt::AlignUnknown,
295+
fill: ' ',
296+
args: args.args,
297+
curarg: args.args.iter(),
298+
};
299+
300+
let mut pieces = args.pieces.iter();
301+
302+
for arg in args.fmt.iter() {
303+
try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
304+
try!(formatter.run(arg));
305+
}
306+
307+
match pieces.next() {
308+
Some(piece) => {
309+
try!(formatter.buf.write(piece.as_bytes()));
310+
}
311+
None => {}
312+
}
313+
314+
Ok(())
315+
}
316+
317+
#[cfg(stage0)] #[doc(hidden)]
266318
pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
267319
let mut formatter = Formatter {
268320
flags: 0,
@@ -285,7 +337,26 @@ impl<'a> Formatter<'a> {
285337
// First up is the collection of functions used to execute a format string
286338
// at runtime. This consumes all of the compile-time statics generated by
287339
// the format! syntax extension.
340+
#[cfg(not(stage0))]
341+
fn run(&mut self, arg: &rt::Argument) -> Result {
342+
// Fill in the format parameters into the formatter
343+
self.fill = arg.format.fill;
344+
self.align = arg.format.align;
345+
self.flags = arg.format.flags;
346+
self.width = self.getcount(&arg.format.width);
347+
self.precision = self.getcount(&arg.format.precision);
348+
349+
// Extract the correct argument
350+
let value = match arg.position {
351+
rt::ArgumentNext => { *self.curarg.next().unwrap() }
352+
rt::ArgumentIs(i) => self.args[i],
353+
};
354+
355+
// Then actually do some printing
356+
(value.formatter)(value.value, self)
357+
}
288358

359+
#[cfg(stage0)] #[doc(hidden)]
289360
fn run(&mut self, piece: &rt::Piece) -> Result {
290361
match *piece {
291362
rt::String(s) => self.buf.write(s.as_bytes()),

src/libcore/fmt/rt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
//! These definitions are similar to their `ct` equivalents, but differ in that
1515
//! these can be statically allocated and are slightly optimized for the runtime
1616
17-
17+
#[cfg(stage0)]
1818
#[doc(hidden)]
1919
pub enum Piece<'a> {
2020
String(&'a str),

src/libsyntax/ext/format.rs

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ struct Context<'a, 'b:'a> {
4949
name_types: HashMap<String, ArgumentType>,
5050
name_ordering: Vec<String>,
5151

52-
/// The latest consecutive literal strings
53-
literal: Option<String>,
52+
/// The latest consecutive literal strings, or empty if there weren't any.
53+
literal: String,
5454

55-
/// Collection of the compiled `rt::Piece` structures
55+
/// Collection of the compiled `rt::Argument` structures
5656
pieces: Vec<Gc<ast::Expr>>,
57+
/// Collection of string literals
58+
str_pieces: Vec<Gc<ast::Expr>>,
5759
name_positions: HashMap<String, uint>,
5860
method_statics: Vec<Gc<ast::Item>>,
5961

@@ -370,28 +372,22 @@ impl<'a, 'b> Context<'a, 'b> {
370372
}
371373
}
372374

373-
/// Translate the accumulated string literals to a static `rt::Piece`
374-
fn trans_literal_string(&mut self) -> Option<Gc<ast::Expr>> {
375+
/// Translate the accumulated string literals to a literal expression
376+
fn trans_literal_string(&mut self) -> Gc<ast::Expr> {
375377
let sp = self.fmtsp;
376-
self.literal.take().map(|s| {
377-
let s = token::intern_and_get_ident(s.as_slice());
378-
self.ecx.expr_call_global(sp,
379-
self.rtpath("String"),
380-
vec!(
381-
self.ecx.expr_str(sp, s)
382-
))
383-
})
378+
let s = token::intern_and_get_ident(self.literal.as_slice());
379+
self.literal.clear();
380+
self.ecx.expr_str(sp, s)
384381
}
385382

386-
/// Translate a `parse::Piece` to a static `rt::Piece`
383+
/// Translate a `parse::Piece` to a static `rt::Argument` or append
384+
/// to the `literal` string.
387385
fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> {
386+
// let mut is_not_default = true;
388387
let sp = self.fmtsp;
389388
match *piece {
390389
parse::String(s) => {
391-
match self.literal {
392-
Some(ref mut sb) => sb.push_str(s),
393-
ref mut empty => *empty = Some(String::from_str(s)),
394-
}
390+
self.literal.push_str(s);
395391
None
396392
}
397393
parse::Argument(ref arg) => {
@@ -450,10 +446,9 @@ impl<'a, 'b> Context<'a, 'b> {
450446
self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
451447

452448
let path = self.ecx.path_global(sp, self.rtpath("Argument"));
453-
let s = self.ecx.expr_struct(sp, path, vec!(
449+
Some(self.ecx.expr_struct(sp, path, vec!(
454450
self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
455-
self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt)));
456-
Some(self.ecx.expr_call_global(sp, self.rtpath("Argument"), vec!(s)))
451+
self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))))
457452
}
458453
}
459454
}
@@ -476,14 +471,38 @@ impl<'a, 'b> Context<'a, 'b> {
476471

477472
// Next, build up the static array which will become our precompiled
478473
// format "string"
474+
let fmt = self.ecx.expr_vec(self.fmtsp, self.str_pieces.clone());
475+
let piece_ty = self.ecx.ty_rptr(self.fmtsp,
476+
self.ecx.ty_ident(self.fmtsp,
477+
self.ecx.ident_of("str")),
478+
Some(self.ecx.lifetime(self.fmtsp,
479+
self.ecx.ident_of(
480+
"'static").name)),
481+
ast::MutImmutable);
482+
483+
let ty = ast::TyFixedLengthVec(
484+
piece_ty,
485+
self.ecx.expr_uint(self.fmtsp, self.str_pieces.len())
486+
);
487+
let ty = self.ecx.ty(self.fmtsp, ty);
488+
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
489+
let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
490+
let item = self.ecx.item(self.fmtsp, static_str_name,
491+
self.static_attrs(), st);
492+
let decl = respan(self.fmtsp, ast::DeclItem(item));
493+
lets.push(box(GC) respan(self.fmtsp,
494+
ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
495+
496+
// Then, build up the static array which will become our precompiled
497+
// format "string"
479498
let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
480499
let piece_ty = self.ecx.ty_path(self.ecx.path_all(
481500
self.fmtsp,
482501
true, vec!(
483502
self.ecx.ident_of("std"),
484503
self.ecx.ident_of("fmt"),
485504
self.ecx.ident_of("rt"),
486-
self.ecx.ident_of("Piece")),
505+
self.ecx.ident_of("Argument")),
487506
vec!(self.ecx.lifetime(self.fmtsp,
488507
self.ecx.ident_of("'static").name)),
489508
Vec::new()
@@ -494,8 +513,8 @@ impl<'a, 'b> Context<'a, 'b> {
494513
);
495514
let ty = self.ecx.ty(self.fmtsp, ty);
496515
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
497-
let static_name = self.ecx.ident_of("__STATIC_FMTSTR");
498-
let item = self.ecx.item(self.fmtsp, static_name,
516+
let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
517+
let item = self.ecx.item(self.fmtsp, static_args_name,
499518
self.static_attrs(), st);
500519
let decl = respan(self.fmtsp, ast::DeclItem(item));
501520
lets.push(box(GC) respan(self.fmtsp,
@@ -545,13 +564,14 @@ impl<'a, 'b> Context<'a, 'b> {
545564
}
546565

547566
// Now create the fmt::Arguments struct with all our locals we created.
548-
let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
567+
let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
568+
let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
549569
let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
550570
let result = self.ecx.expr_call_global(self.fmtsp, vec!(
551571
self.ecx.ident_of("std"),
552572
self.ecx.ident_of("fmt"),
553573
self.ecx.ident_of("Arguments"),
554-
self.ecx.ident_of("new")), vec!(fmt, args_slice));
574+
self.ecx.ident_of("new")), vec!(pieces, fmt, args_slice));
555575

556576
// We did all the work of making sure that the arguments
557577
// 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,
718738
name_ordering: name_ordering,
719739
nest_level: 0,
720740
next_arg: 0,
721-
literal: None,
741+
literal: String::new(),
722742
pieces: Vec::new(),
743+
str_pieces: Vec::new(),
723744
method_statics: Vec::new(),
724745
fmtsp: sp,
725746
};
@@ -739,8 +760,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
739760
cx.verify_piece(&piece);
740761
match cx.trans_piece(&piece) {
741762
Some(piece) => {
742-
cx.trans_literal_string().map(|piece|
743-
cx.pieces.push(piece));
763+
let s = cx.trans_literal_string();
764+
cx.str_pieces.push(s);
744765
cx.pieces.push(piece);
745766
}
746767
None => {}
@@ -758,7 +779,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
758779
}
759780
None => {}
760781
}
761-
cx.trans_literal_string().map(|piece| cx.pieces.push(piece));
782+
if !cx.literal.is_empty() {
783+
let s = cx.trans_literal_string();
784+
cx.str_pieces.push(s);
785+
}
762786

763787
// Make sure that all arguments were used and all arguments have types.
764788
for (i, ty) in cx.arg_types.iter().enumerate() {

src/test/pretty/issue-4264.pp

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,20 @@
3939
() => {
4040
#[inline]
4141
#[allow(dead_code)]
42-
static __STATIC_FMTSTR:
43-
[::std::fmt::rt::Piece<'static>, ..(1u as uint)] =
44-
([((::std::fmt::rt::String as
45-
fn(&'static str) -> core::fmt::rt::Piece<'static>)(("test"
46-
as
47-
&'static str))
48-
as core::fmt::rt::Piece<'static>)] as
49-
[core::fmt::rt::Piece<'static>, .. 1]);
42+
static __STATIC_FMTSTR: [&'static str, ..(1u as uint)] =
43+
([("test" as &'static str)] as [&'static str, .. 1]);
5044
let __args_vec =
5145
(&([] as [core::fmt::Argument<'_>, .. 0]) as
5246
&[core::fmt::Argument<'_>, .. 0]);
5347
let __args =
5448
(unsafe {
5549
((::std::fmt::Arguments::new as
56-
unsafe fn(&'static [core::fmt::rt::Piece<'static>], &'a [core::fmt::Argument<'a>]) -> core::fmt::Arguments<'a>)((__STATIC_FMTSTR
57-
as
58-
[core::fmt::rt::Piece<'static>, .. 1]),
59-
(__args_vec
60-
as
61-
&[core::fmt::Argument<'_>, .. 0]))
50+
unsafe fn(&'static [&'static str], &'a [core::fmt::Argument<'a>]) -> core::fmt::Arguments<'a>)((__STATIC_FMTSTR
51+
as
52+
[&'static str, .. 1]),
53+
(__args_vec
54+
as
55+
&[core::fmt::Argument<'_>, .. 0]))
6256
as core::fmt::Arguments<'_>)
6357
} as core::fmt::Arguments<'_>);
6458

0 commit comments

Comments
 (0)