Skip to content

Commit dd29bd1

Browse files
committed
Eagerly expand format_args macro
This allows format_args!(some_macro!(...), ...) to compile. gcc/rust/ChangeLog: * expand/rust-macro-builtins-format-args.cc (format_args_parse_arguments): Split format expression parsing into... (format_args_parse_expr): ...a new function here, while handling eager expansion. (MacroBuiltin::format_args_handler): Use format_args_parse_expr. gcc/testsuite/ChangeLog: * rust/compile/format_args_concat.rs: New test. Signed-off-by: Owen Avery <[email protected]>
1 parent adeb82c commit dd29bd1

File tree

2 files changed

+99
-30
lines changed

2 files changed

+99
-30
lines changed

gcc/rust/expand/rust-macro-builtins-format-args.cc

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,39 +37,42 @@ struct FormatArgsParseError
3737
} kind;
3838
};
3939

40-
static tl::expected<FormatArgsInput, FormatArgsParseError>
41-
format_args_parse_arguments (AST::MacroInvocData &invoc)
40+
static inline tl::expected<std::string, AST::Fragment>
41+
format_args_parse_expr (location_t invoc_locus, AST::MacroInvocData &invoc,
42+
Parser<MacroInvocLexer> &parser,
43+
BuiltinMacro macro_kind)
4244
{
43-
MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
44-
Parser<MacroInvocLexer> parser (lex);
45-
46-
// TODO: check if EOF - return that format_args!() requires at least one
47-
// argument
48-
49-
auto args = AST::FormatArguments ();
50-
auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
51-
std::unique_ptr<AST::Expr> format_expr = nullptr;
45+
std::unique_ptr<AST::Expr> format_expr = parser.parse_expr ();
46+
rust_assert (format_expr);
5247

53-
// TODO: Handle the case where we're not parsing a string literal (macro
54-
// invocation for e.g.)
55-
switch (parser.peek_current_token ()->get_id ())
48+
if (format_expr->get_expr_kind () == AST::Expr::Kind::MacroInvocation)
5649
{
57-
case STRING_LITERAL:
58-
case RAW_STRING_LITERAL:
59-
format_expr = parser.parse_literal_expr ();
60-
default:
61-
// do nothing
62-
;
50+
std::vector<std::unique_ptr<AST::MacroInvocation>> pending;
51+
pending.emplace_back (
52+
static_cast<AST::MacroInvocation *> (format_expr.release ()));
53+
return tl::unexpected (
54+
make_eager_builtin_invocation (macro_kind, invoc_locus,
55+
invoc.get_delim_tok_tree (),
56+
std::move (pending)));
6357
}
6458

65-
rust_assert (format_expr);
66-
6759
// TODO(Arthur): Clean this up - if we haven't parsed a string literal but a
6860
// macro invocation, what do we do here? return a tl::unexpected?
69-
auto format_str = static_cast<AST::LiteralExpr &> (*format_expr)
70-
.get_literal ()
71-
.as_string ();
61+
rust_assert (format_expr->is_literal ());
62+
return static_cast<AST::LiteralExpr &> (*format_expr)
63+
.get_literal ()
64+
.as_string ();
65+
}
66+
67+
static inline tl::expected<AST::FormatArguments, FormatArgsParseError>
68+
format_args_parse_arguments (AST::MacroInvocData &invoc,
69+
Parser<MacroInvocLexer> &parser,
70+
TokenId last_token_id)
71+
{
72+
// TODO: check if EOF - return that format_args!() requires at least one
73+
// argument
7274

75+
auto args = AST::FormatArguments ();
7376
// TODO: Allow implicit captures ONLY if the the first arg is a string literal
7477
// and not a macro invocation
7578

@@ -126,7 +129,7 @@ format_args_parse_arguments (AST::MacroInvocData &invoc)
126129
// we need to skip commas, don't we?
127130
}
128131

129-
return FormatArgsInput{std::move (format_str), std::move (args)};
132+
return args;
130133
}
131134

132135
tl::optional<AST::Fragment>
@@ -135,9 +138,24 @@ MacroBuiltin::format_args_handler (location_t invoc_locus,
135138
AST::InvocKind semicolon,
136139
AST::FormatArgs::Newline nl)
137140
{
138-
auto input = format_args_parse_arguments (invoc);
141+
MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
142+
Parser<MacroInvocLexer> parser (lex);
143+
144+
auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
145+
146+
auto format_str = format_args_parse_expr (invoc_locus, invoc, parser,
147+
nl == AST::FormatArgs::Newline::Yes
148+
? BuiltinMacro::FormatArgsNl
149+
: BuiltinMacro::FormatArgs);
150+
151+
if (!format_str)
152+
{
153+
return std::move (format_str.error ());
154+
}
155+
156+
auto args = format_args_parse_arguments (invoc, parser, last_token_id);
139157

140-
if (!input)
158+
if (!args)
141159
{
142160
rust_error_at (invoc_locus,
143161
"could not parse arguments to %<format_args!()%>");
@@ -173,7 +191,7 @@ MacroBuiltin::format_args_handler (location_t invoc_locus,
173191

174192
bool append_newline = nl == AST::FormatArgs::Newline::Yes;
175193

176-
auto fmt_str = std::move (input->format_str);
194+
auto fmt_str = std::move (format_str.value ());
177195
if (append_newline)
178196
fmt_str += '\n';
179197

@@ -189,7 +207,7 @@ MacroBuiltin::format_args_handler (location_t invoc_locus,
189207
// for creating the `template`
190208

191209
auto fmt_args_node = AST::FormatArgs (invoc_locus, std::move (pieces),
192-
std::move (input->args));
210+
std::move (args.value ()));
193211

194212
auto expanded
195213
= Fmt::expand_format_args (fmt_args_node,
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#![feature(rustc_attrs)]
2+
3+
#[rustc_builtin_macro]
4+
macro_rules! format_args {
5+
() => {};
6+
}
7+
8+
#[rustc_builtin_macro]
9+
macro_rules! concat {
10+
() => {};
11+
}
12+
13+
#[lang = "sized"]
14+
trait Sized {}
15+
16+
pub mod core {
17+
pub mod fmt {
18+
pub struct Formatter;
19+
pub struct Result;
20+
21+
pub struct Arguments<'a>;
22+
23+
impl<'a> Arguments<'a> {
24+
pub fn new_v1(_: &'a [&'static str], _: &'a [ArgumentV1<'a>]) -> Arguments<'a> {
25+
Arguments
26+
}
27+
}
28+
29+
pub struct ArgumentV1<'a>;
30+
31+
impl<'a> ArgumentV1<'a> {
32+
pub fn new<'b, T>(_: &'b T, _: fn(&T, &mut Formatter) -> Result) -> ArgumentV1 {
33+
ArgumentV1
34+
}
35+
}
36+
37+
pub trait Display {
38+
fn fmt(&self, _: &mut Formatter) -> Result;
39+
}
40+
41+
impl Display for i32 {
42+
fn fmt(&self, _: &mut Formatter) -> Result {
43+
Result
44+
}
45+
}
46+
}
47+
}
48+
49+
fn main() {
50+
let _formatted = format_args!(concat!("hello ", "{}"), 15);
51+
}

0 commit comments

Comments
 (0)