Skip to content

Commit 854d95f

Browse files
committed
syntax: Add a macro, format_args_method!()
Currently, the format_args!() macro takes as its first argument an expression which is the callee of an ExprCall. This means that if format_args!() is used with calling a method a closure must be used. Consider this code, however: format_args!(|args| { foo.writer.write_fmt(args) }, "{}", foo.field) The closure borrows the entire `foo` structure, disallowing the later borrow of `foo.field`. To preserve the semantics of the `write!` macro, it is also impossible to borrow specifically the `writer` field of the `foo` structure because it must be borrowed mutably, but the `foo` structure is not guaranteed to be mutable itself. This new macro is invoked like: format_args_method!(foo.writer, write_fmt, "{}", foo.field) This macro will generate an ExprMethodCall which allows the borrow checker to understand that `writer` and `field` should be borrowed separately. This macro is not strictly necessary, with DST or possibly UFCS other workarounds could be used. For now, though, it looks like this is required to implement the `write!` macro.
1 parent 00f9263 commit 854d95f

File tree

3 files changed

+62
-29
lines changed

3 files changed

+62
-29
lines changed

src/libsyntax/ext/base.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,10 @@ pub fn syntax_expander_table() -> SyntaxEnv {
281281
ext::fmt::expand_syntax_ext));
282282
syntax_expanders.insert(intern("format_args"),
283283
builtin_normal_expander(
284-
ext::format::expand_args));
284+
ext::format::expand_format_args));
285+
syntax_expanders.insert(intern("format_args_method"),
286+
builtin_normal_expander(
287+
ext::format::expand_format_args_method));
285288
syntax_expanders.insert(intern("env"),
286289
builtin_normal_expander(
287290
ext::env::expand_env));

src/libsyntax/ext/deriving/show.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,23 +120,18 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
120120
// AST construction!
121121
// we're basically calling
122122
//
123-
// format_arg!(|__args| ::std::fmt::write(fmt.buf, __args), "<format_string>", exprs...)
123+
// format_arg_method!(fmt, write_fmt, "<format_string>", exprs...)
124124
//
125125
// but doing it directly via ext::format.
126126
let formatter = substr.nonself_args[0];
127-
let buf = cx.expr_field_access(span, formatter, cx.ident_of("buf"));
128-
129-
let std_write = vec!(cx.ident_of("std"), cx.ident_of("fmt"), cx.ident_of("write"));
130-
let args = cx.ident_of("__args");
131-
let write_call = cx.expr_call_global(span, std_write, vec!(buf, cx.expr_ident(span, args)));
132-
let format_closure = cx.lambda_expr(span, vec!(args), write_call);
133127

128+
let meth = cx.ident_of("write_fmt");
134129
let s = token::intern_and_get_ident(format_string.as_slice());
135130
let format_string = cx.expr_str(span, s);
136131

137132
// phew, not our responsibility any more!
138133
format::expand_preparsed_format_args(cx, span,
139-
format_closure,
134+
format::MethodCall(formatter, meth),
140135
format_string, exprs, Vec::new(),
141136
HashMap::new())
142137
}

src/libsyntax/ext/format.rs

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ struct Context<'a, 'b> {
5959
next_arg: uint,
6060
}
6161

62+
pub enum Invocation {
63+
Call(@ast::Expr),
64+
MethodCall(@ast::Expr, ast::Ident),
65+
}
66+
6267
/// Parses the arguments from the given list of tokens, returning None
6368
/// if there's a parse error so we can continue parsing other format!
6469
/// expressions.
@@ -67,8 +72,9 @@ struct Context<'a, 'b> {
6772
///
6873
/// Some((fmtstr, unnamed arguments, ordering of named arguments,
6974
/// named arguments))
70-
fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
71-
-> (@ast::Expr, Option<(@ast::Expr, Vec<@ast::Expr>, Vec<StrBuf>,
75+
fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool,
76+
tts: &[ast::TokenTree])
77+
-> (Invocation, Option<(@ast::Expr, Vec<@ast::Expr>, Vec<StrBuf>,
7278
HashMap<StrBuf, @ast::Expr>)>) {
7379
let mut args = Vec::new();
7480
let mut names = HashMap::<StrBuf, @ast::Expr>::new();
@@ -80,22 +86,31 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
8086
.map(|x| (*x).clone())
8187
.collect());
8288
// Parse the leading function expression (maybe a block, maybe a path)
83-
let extra = p.parse_expr();
89+
let invocation = if allow_method {
90+
let e = p.parse_expr();
91+
if !p.eat(&token::COMMA) {
92+
ecx.span_err(sp, "expected token: `,`");
93+
return (Call(e), None);
94+
}
95+
MethodCall(e, p.parse_ident())
96+
} else {
97+
Call(p.parse_expr())
98+
};
8499
if !p.eat(&token::COMMA) {
85100
ecx.span_err(sp, "expected token: `,`");
86-
return (extra, None);
101+
return (invocation, None);
87102
}
88103

89104
if p.token == token::EOF {
90105
ecx.span_err(sp, "requires at least a format string argument");
91-
return (extra, None);
106+
return (invocation, None);
92107
}
93108
let fmtstr = p.parse_expr();
94109
let mut named = false;
95110
while p.token != token::EOF {
96111
if !p.eat(&token::COMMA) {
97112
ecx.span_err(sp, "expected token: `,`");
98-
return (extra, None);
113+
return (invocation, None);
99114
}
100115
if p.token == token::EOF { break } // accept trailing commas
101116
if named || (token::is_ident(&p.token) &&
@@ -110,13 +125,13 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
110125
ecx.span_err(p.span,
111126
"expected ident, positional arguments \
112127
cannot follow named arguments");
113-
return (extra, None);
128+
return (invocation, None);
114129
}
115130
_ => {
116131
ecx.span_err(p.span,
117132
format!("expected ident for named argument, but found `{}`",
118133
p.this_token_to_str()));
119-
return (extra, None);
134+
return (invocation, None);
120135
}
121136
};
122137
let interned_name = token::get_ident(ident);
@@ -137,7 +152,7 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
137152
args.push(p.parse_expr());
138153
}
139154
}
140-
return (extra, Some((fmtstr, args, order, names)));
155+
return (invocation, Some((fmtstr, args, order, names)));
141156
}
142157

143158
impl<'a, 'b> Context<'a, 'b> {
@@ -595,7 +610,7 @@ impl<'a, 'b> Context<'a, 'b> {
595610

596611
/// Actually builds the expression which the iformat! block will be expanded
597612
/// to
598-
fn to_expr(&self, extra: @ast::Expr) -> @ast::Expr {
613+
fn to_expr(&self, invocation: Invocation) -> @ast::Expr {
599614
let mut lets = Vec::new();
600615
let mut locals = Vec::new();
601616
let mut names = Vec::from_fn(self.name_positions.len(), |_| None);
@@ -699,8 +714,16 @@ impl<'a, 'b> Context<'a, 'b> {
699714
let resname = self.ecx.ident_of("__args");
700715
lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
701716
let res = self.ecx.expr_ident(self.fmtsp, resname);
702-
let result = self.ecx.expr_call(extra.span, extra, vec!(
703-
self.ecx.expr_addr_of(extra.span, res)));
717+
let result = match invocation {
718+
Call(e) => {
719+
self.ecx.expr_call(e.span, e,
720+
vec!(self.ecx.expr_addr_of(e.span, res)))
721+
}
722+
MethodCall(e, m) => {
723+
self.ecx.expr_method_call(e.span, e, m,
724+
vec!(self.ecx.expr_addr_of(e.span, res)))
725+
}
726+
};
704727
let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
705728
Some(result)));
706729

@@ -794,13 +817,25 @@ impl<'a, 'b> Context<'a, 'b> {
794817
}
795818
}
796819

797-
pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
798-
tts: &[ast::TokenTree]) -> Box<base::MacResult> {
820+
pub fn expand_format_args(ecx: &mut ExtCtxt, sp: Span,
821+
tts: &[ast::TokenTree]) -> Box<base::MacResult> {
822+
823+
match parse_args(ecx, sp, false, tts) {
824+
(invocation, Some((efmt, args, order, names))) => {
825+
MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
826+
args, order, names))
827+
}
828+
(_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
829+
}
830+
}
831+
832+
pub fn expand_format_args_method(ecx: &mut ExtCtxt, sp: Span,
833+
tts: &[ast::TokenTree]) -> Box<base::MacResult> {
799834

800-
match parse_args(ecx, sp, tts) {
801-
(extra, Some((efmt, args, order, names))) => {
802-
MacExpr::new(expand_preparsed_format_args(ecx, sp, extra, efmt, args,
803-
order, names))
835+
match parse_args(ecx, sp, true, tts) {
836+
(invocation, Some((efmt, args, order, names))) => {
837+
MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt,
838+
args, order, names))
804839
}
805840
(_, None) => MacExpr::new(ecx.expr_uint(sp, 2))
806841
}
@@ -810,7 +845,7 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
810845
/// name=names...)` and construct the appropriate formatting
811846
/// expression.
812847
pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
813-
extra: @ast::Expr,
848+
invocation: Invocation,
814849
efmt: @ast::Expr, args: Vec<@ast::Expr>,
815850
name_ordering: Vec<StrBuf>,
816851
names: HashMap<StrBuf, @ast::Expr>) -> @ast::Expr {
@@ -869,5 +904,5 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
869904
}
870905
}
871906

872-
cx.to_expr(extra)
907+
cx.to_expr(invocation)
873908
}

0 commit comments

Comments
 (0)