Skip to content

Commit 9f6299e

Browse files
committed
Implement fmt::Arguments as a closure.
1 parent 5276b1a commit 9f6299e

File tree

3 files changed

+267
-13
lines changed

3 files changed

+267
-13
lines changed
Lines changed: 216 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,231 @@
11
use super::*;
22
use rustc_ast as ast;
3-
use rustc_span::sym;
3+
use rustc_span::symbol::{kw, sym, Ident};
44

55
pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<ast::Expr> {
66
let macsp = ecx.with_def_site_ctxt(ecx.call_site());
77

8-
// TODO
8+
if fmt.template.is_empty() {
9+
return ecx.expr_call_global(
10+
macsp,
11+
ecx.std_path(&[sym::fmt, sym::Arguments, sym::from_static_str]),
12+
vec![ecx.expr_str(macsp, kw::Empty)],
13+
);
14+
}
15+
16+
if let &[FormatArgsPiece::Literal(s)] = &fmt.template[..] {
17+
return ecx.expr_call_global(
18+
macsp,
19+
ecx.std_path(&[sym::fmt, sym::Arguments, sym::from_static_str]),
20+
vec![ecx.expr_str(macsp, s)],
21+
);
22+
}
23+
24+
let args = Ident::new(sym::_args, macsp);
25+
let f = Ident::new(sym::f, macsp);
26+
27+
let arguments = fmt.arguments.into_vec();
28+
29+
let mut statements = Vec::new();
30+
31+
let mut default_options = true;
32+
33+
for piece in fmt.template {
34+
match piece {
35+
FormatArgsPiece::Literal(s) => {
36+
// Generate:
37+
// f.write_str("…")?;
38+
statements.push(ecx.stmt_expr(ecx.expr(
39+
macsp,
40+
ast::ExprKind::Try(ecx.expr(
41+
macsp,
42+
ast::ExprKind::MethodCall(
43+
ast::PathSegment::from_ident(Ident::new(sym::write_str, macsp)),
44+
ecx.expr_ident(macsp, f),
45+
vec![ecx.expr_str(macsp, s)],
46+
macsp,
47+
),
48+
)),
49+
)));
50+
}
51+
FormatArgsPiece::Placeholder(p) => {
52+
// Don't set options if they're still set to defaults
53+
// and this placeholder also uses default options.
54+
let d = p.format_options == FormatOptions::default();
55+
if !default_options || !d {
56+
default_options = d;
57+
// Generate:
58+
// f.set_options(…);
59+
statements.push(ecx.stmt_expr(ecx.expr(
60+
macsp,
61+
ast::ExprKind::MethodCall(
62+
ast::PathSegment::from_ident(Ident::new(sym::set_options, macsp)),
63+
ecx.expr_ident(macsp, f),
64+
vec![
65+
ecx.expr_u32(macsp, p.format_options.flags),
66+
ecx.expr_char(macsp, p.format_options.fill.unwrap_or(' ')),
67+
ecx.expr_path(ecx.path_global(
68+
macsp,
69+
ecx.std_path(&[
70+
sym::fmt,
71+
sym::rt,
72+
sym::v1,
73+
sym::Alignment,
74+
match p.format_options.alignment {
75+
Some(FormatAlignment::Left) => sym::Left,
76+
Some(FormatAlignment::Right) => sym::Right,
77+
Some(FormatAlignment::Center) => sym::Center,
78+
None => sym::Unknown,
79+
},
80+
]),
81+
)),
82+
make_count(ecx, macsp, &arguments, args, p.format_options.width),
83+
make_count(
84+
ecx,
85+
macsp,
86+
&arguments,
87+
args,
88+
p.format_options.precision,
89+
),
90+
],
91+
macsp,
92+
),
93+
)));
94+
}
95+
// Generate:
96+
// ::core::fmt::Display::fmt(arg.0, f)?;
97+
let arg = if let Ok(i) = p.argument.index {
98+
ecx.expr_field(
99+
arguments[i].expr.span.with_ctxt(macsp.ctxt()),
100+
ecx.expr_ident(macsp, args),
101+
Ident::new(sym::integer(i), macsp),
102+
)
103+
} else {
104+
DummyResult::raw_expr(macsp, true)
105+
};
106+
let fmt_trait = match p.format_trait {
107+
FormatTrait::Display => sym::Display,
108+
FormatTrait::Debug => sym::Debug,
109+
FormatTrait::LowerExp => sym::LowerExp,
110+
FormatTrait::UpperExp => sym::UpperExp,
111+
FormatTrait::Octal => sym::Octal,
112+
FormatTrait::Pointer => sym::Pointer,
113+
FormatTrait::Binary => sym::Binary,
114+
FormatTrait::LowerHex => sym::LowerHex,
115+
FormatTrait::UpperHex => sym::UpperHex,
116+
};
117+
statements.push(ecx.stmt_expr(ecx.expr(
118+
macsp,
119+
ast::ExprKind::Try(ecx.expr_call_global(
120+
arg.span,
121+
ecx.std_path(&[sym::fmt, fmt_trait, sym::fmt]),
122+
vec![arg, ecx.expr_ident(macsp, f)],
123+
)),
124+
)));
125+
}
126+
}
127+
}
128+
129+
// Generate:
130+
// Ok(())
131+
statements.push(ecx.stmt_expr(ecx.expr_ok(macsp, ecx.expr_tuple(macsp, Vec::new()))));
132+
133+
// Generate:
134+
// |f: &mut ::core::fmt::Formatter| -> ::core::fmt::Result {
135+
// … // statements
136+
// }
137+
let closure = ecx.expr(
138+
macsp,
139+
ast::ExprKind::Closure(
140+
ast::ClosureBinder::NotPresent,
141+
ast::CaptureBy::Ref,
142+
ast::Async::No,
143+
ast::Movability::Movable,
144+
ecx.fn_decl(
145+
vec![ecx.param(
146+
macsp,
147+
f,
148+
ecx.ty_rptr(
149+
macsp,
150+
ecx.ty_path(ecx.path_all(
151+
macsp,
152+
true,
153+
ecx.std_path(&[sym::fmt, sym::Formatter]),
154+
vec![ast::GenericArg::Lifetime(ast::Lifetime {
155+
id: ast::DUMMY_NODE_ID,
156+
ident: Ident::new(kw::UnderscoreLifetime, macsp),
157+
})],
158+
)),
159+
None,
160+
ast::Mutability::Mut,
161+
),
162+
)],
163+
ast::FnRetTy::Ty(
164+
ecx.ty_path(ecx.path_global(macsp, ecx.std_path(&[sym::fmt, sym::Result]))),
165+
),
166+
),
167+
ecx.expr_block(ecx.block(macsp, statements)),
168+
macsp,
169+
),
170+
);
9171

10172
// Generate:
11173
// ::core::fmt::Arguments::new(
12-
// …
174+
// &match (&arg0, &arg1, …) {
175+
// args => closure,
176+
// }
13177
// )
14178
ecx.expr_call_global(
15179
macsp,
16180
ecx.std_path(&[sym::fmt, sym::Arguments, sym::new]),
17-
vec![],
181+
vec![
182+
ecx.expr_addr_of(
183+
macsp,
184+
ecx.expr_match(
185+
macsp,
186+
ecx.expr_tuple(
187+
macsp,
188+
arguments
189+
.into_iter()
190+
.map(|arg| {
191+
ecx.expr_addr_of(arg.expr.span.with_ctxt(macsp.ctxt()), arg.expr)
192+
})
193+
.collect(),
194+
),
195+
vec![ecx.arm(macsp, ecx.pat_ident(macsp, args), closure)],
196+
),
197+
),
198+
],
18199
)
19200
}
201+
202+
pub fn make_count(
203+
ecx: &ExtCtxt<'_>,
204+
macsp: Span,
205+
arguments: &[FormatArgument],
206+
args: Ident,
207+
count: Option<FormatCount>,
208+
) -> P<ast::Expr> {
209+
match count {
210+
Some(FormatCount::Literal(n)) => ecx.expr_some(macsp, ecx.expr_usize(macsp, n)),
211+
Some(FormatCount::Argument(arg)) => {
212+
if let Ok(i) = arg.index {
213+
let sp = arguments[i].expr.span.with_ctxt(macsp.ctxt());
214+
ecx.expr_some(
215+
sp,
216+
ecx.expr_deref(
217+
sp,
218+
ecx.expr_field(
219+
sp,
220+
ecx.expr_ident(macsp, args),
221+
Ident::new(sym::integer(i), macsp),
222+
),
223+
),
224+
)
225+
} else {
226+
DummyResult::raw_expr(macsp, true)
227+
}
228+
}
229+
None => ecx.expr_none(macsp),
230+
}
231+
}

compiler/rustc_span/src/symbol.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ symbols! {
155155
BTreeEntry,
156156
BTreeMap,
157157
BTreeSet,
158+
Binary,
158159
BinaryHeap,
159160
Borrow,
160161
BorrowMut,
@@ -223,6 +224,8 @@ symbols! {
223224
Left,
224225
LinkedList,
225226
LintPass,
227+
LowerExp,
228+
LowerHex,
226229
Mutex,
227230
MutexGuard,
228231
N,
@@ -237,6 +240,7 @@ symbols! {
237240
NonZeroU64,
238241
NonZeroU8,
239242
None,
243+
Octal,
240244
Ok,
241245
Option,
242246
Ord,
@@ -299,6 +303,8 @@ symbols! {
299303
TyKind,
300304
Unknown,
301305
UnsafeArg,
306+
UpperExp,
307+
UpperHex,
302308
Vec,
303309
VecDeque,
304310
Wrapper,
@@ -310,6 +316,7 @@ symbols! {
310316
__S,
311317
__awaitee,
312318
__try_var,
319+
_args,
313320
_d,
314321
_e,
315322
_task_context,
@@ -754,6 +761,7 @@ symbols! {
754761
from_output,
755762
from_residual,
756763
from_size_align_unchecked,
764+
from_static_str,
757765
from_usize,
758766
from_yeet,
759767
fsub_fast,
@@ -1317,6 +1325,7 @@ symbols! {
13171325
self_in_typedefs,
13181326
self_struct_ctor,
13191327
semitransparent,
1328+
set_options,
13201329
shadow_call_stack,
13211330
shl,
13221331
shl_assign,

library/core/src/fmt/mod.rs

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,25 @@ impl<'a> Formatter<'a> {
250250
buf,
251251
}
252252
}
253+
254+
/// Set all the formatting options.
255+
#[unstable(feature = "fmt_internals", issue = "none")]
256+
#[doc(hidden)]
257+
#[inline]
258+
pub fn set_options(
259+
&mut self,
260+
flags: u32,
261+
fill: char,
262+
align: rt::v1::Alignment,
263+
width: Option<usize>,
264+
precision: Option<usize>,
265+
) {
266+
self.flags = flags;
267+
self.fill = fill;
268+
self.align = align;
269+
self.width = width;
270+
self.precision = precision;
271+
}
253272
}
254273

255274
// NB. Argument is essentially an optimized partially applied formatting function,
@@ -435,10 +454,8 @@ impl<'a> Arguments<'a> {
435454
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
436455
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
437456
#[cfg(not(bootstrap))]
438-
pub const fn new(
439-
// TODO
440-
) -> Arguments<'a> {
441-
unimplemented!() // TODO
457+
pub const fn new(f: &'a dyn Fn(&mut Formatter<'_>) -> Result) -> Arguments<'a> {
458+
Arguments { inner: Inner::Fn(f) }
442459
}
443460

444461
#[doc(hidden)]
@@ -447,7 +464,7 @@ impl<'a> Arguments<'a> {
447464
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
448465
#[cfg(not(bootstrap))]
449466
pub const fn from_static_str(s: &'static str) -> Arguments<'a> {
450-
unimplemented!() // TODO
467+
Arguments { inner: Inner::StaticStr(s) }
451468
}
452469

453470
/// Estimates the length of the formatted text.
@@ -459,7 +476,10 @@ impl<'a> Arguments<'a> {
459476
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
460477
#[cfg(not(bootstrap))]
461478
pub fn estimated_capacity(&self) -> usize {
462-
unimplemented!() // TODO
479+
match self.inner {
480+
Inner::Fn(_) => 0, // FIXME
481+
Inner::StaticStr(s) => s.len(),
482+
}
463483
}
464484

465485
/// Old estimated_capacity().
@@ -513,7 +533,14 @@ impl<'a> Arguments<'a> {
513533
#[derive(Copy, Clone)]
514534
#[cfg(not(bootstrap))]
515535
pub struct Arguments<'a> {
516-
// TODO
536+
inner: Inner<'a>,
537+
}
538+
539+
#[cfg(not(bootstrap))]
540+
#[derive(Copy, Clone)]
541+
enum Inner<'a> {
542+
Fn(&'a dyn Fn(&mut Formatter<'_>) -> Result),
543+
StaticStr(&'static str),
517544
}
518545

519546
/// Old fmt::Arguments.
@@ -565,7 +592,10 @@ impl<'a> Arguments<'a> {
565592
#[inline]
566593
#[cfg(not(bootstrap))]
567594
pub const fn as_str(&self) -> Option<&'static str> {
568-
unimplemented!() // TODO
595+
match self.inner {
596+
Inner::Fn(_) => None,
597+
Inner::StaticStr(s) => Some(s),
598+
}
569599
}
570600

571601
/// Old as_str().
@@ -1248,7 +1278,10 @@ pub trait UpperExp {
12481278
#[stable(feature = "rust1", since = "1.0.0")]
12491279
#[cfg(not(bootstrap))]
12501280
pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
1251-
unimplemented!() // TODO
1281+
match args.inner {
1282+
Inner::Fn(f) => f(&mut Formatter::new(output)),
1283+
Inner::StaticStr(s) => output.write_str(s),
1284+
}
12521285
}
12531286

12541287
/// Old write().

0 commit comments

Comments
 (0)