Skip to content

Commit c5631d1

Browse files
committed
Allow format_args! with a macro as first argument
Fixes #3896. FormatArgsPieces isn't great, but I don't see a better alternative for now. gcc/rust/ChangeLog: * ast/rust-ast-visitor.cc (DefaultASTVisitor::visit): Visit first argument of FormatArgs. * ast/rust-ast.cc (FormatArgs::clone_expr_impl): Remove usage of std::cerr for debugging. * ast/rust-builtin-ast-nodes.h (enum class FormatArgsNewline): New enum. (class FormatArgsPieces): New class. (FormatArgs::Newline): Replace with using declaration referencing FormatArgsNewline. (FormatArgs::FormatArgs): Accept a FormatArgsPieces instance. (FormatArgs::get_template_arg): New member function. (FormatArgs::get_template): Handle changes to class fields. (FormatArgs::template_pieces): Rename to... (FormatArgs::template_arg): ...here and change type to FormatArgsPieces. * expand/rust-expand-format-args.cc: Include "rust-expand-visitor.h". (expand_format_first_arg): New static function. (expand_format_args): Change signature. * expand/rust-expand-format-args.h: Include "rust-system.h" and adjust include order. (class ExpandVisitor): Forward declare. (struct MacroExpander): Likewise. (expand_format_args): Change signature. * expand/rust-expand-visitor.cc: Include "rust-expand-format-args.h". (ExpandVisitor::visit): Attempt to expand FormatArgs. * expand/rust-expand-visitor.h: Include "rust-ast-full-decls.h". (ExpandVisitor::visit): New overload for FormatArgs. * expand/rust-macro-builtins-format-args.cc (FormatArgsInput::format_str): Rename to... (FormatArgsInput::format_expr): ...here and change type to std::unique_ptr<Expr>. (format_args_parse_arguments): Handle change to FormatArgsInput. (MacroBuiltin::format_args_handler): Likewise and handle changes to FormatArgs. gcc/testsuite/ChangeLog: * rust/compile/format_args_concat.rs: New test. Signed-off-by: Owen Avery <[email protected]>
1 parent 5b7a0d7 commit c5631d1

9 files changed

+295
-103
lines changed

gcc/rust/ast/rust-ast-visitor.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1500,9 +1500,12 @@ DefaultASTVisitor::visit (AST::BareFunctionType &type)
15001500
}
15011501

15021502
void
1503-
DefaultASTVisitor::visit (AST::FormatArgs &)
1503+
DefaultASTVisitor::visit (AST::FormatArgs &fmt)
15041504
{
15051505
// FIXME: Do we have anything to do? any subnodes to visit? Probably, right?
1506+
auto &fmt_arg = fmt.get_template_arg ();
1507+
if (fmt_arg.has_expr ())
1508+
visit (fmt_arg.get_expr ());
15061509
}
15071510

15081511
void

gcc/rust/ast/rust-ast.cc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5025,8 +5025,6 @@ FormatArgs::set_outer_attrs (std::vector<Attribute>)
50255025
Expr *
50265026
FormatArgs::clone_expr_impl () const
50275027
{
5028-
std::cerr << "[ARTHUR] cloning FormatArgs! " << std::endl;
5029-
50305028
return new FormatArgs (*this);
50315029
}
50325030

gcc/rust/ast/rust-builtin-ast-nodes.h

Lines changed: 145 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,141 @@ class FormatArguments
168168
std::vector<FormatArgument> args;
169169
};
170170

171+
enum class FormatArgsNewline
172+
{
173+
Yes,
174+
No
175+
};
176+
177+
// can be either an expression or Fmt::Pieces
178+
// used to handle `format_args!(some_macro!(...), ...)`
179+
class FormatArgsPieces final
180+
{
181+
public:
182+
using Newline = FormatArgsNewline;
183+
184+
enum class Kind
185+
{
186+
EXPR,
187+
PIECES
188+
};
189+
190+
FormatArgsPieces (std::unique_ptr<Expr> expr, Newline nl)
191+
: kind (Kind::EXPR), expr_nl (std::move (expr), nl)
192+
{}
193+
194+
FormatArgsPieces (Fmt::Pieces template_str)
195+
: kind (Kind::PIECES), template_str (std::move (template_str))
196+
{}
197+
198+
FormatArgsPieces (const FormatArgsPieces &other) noexcept : kind (other.kind)
199+
{
200+
switch (kind)
201+
{
202+
case Kind::EXPR:
203+
new (&expr_nl) std::pair<std::unique_ptr<Expr>, Newline> (
204+
other.expr_nl.first->clone_expr (), other.expr_nl.second);
205+
break;
206+
case Kind::PIECES:
207+
new (&template_str) Fmt::Pieces (other.template_str);
208+
break;
209+
default:
210+
rust_unreachable ();
211+
}
212+
}
213+
214+
FormatArgsPieces (FormatArgsPieces &&other) noexcept : kind (other.kind)
215+
{
216+
switch (kind)
217+
{
218+
case Kind::EXPR:
219+
new (&expr_nl) std::pair<std::unique_ptr<Expr>, Newline> (
220+
std::move (other.expr_nl.first), other.expr_nl.second);
221+
break;
222+
case Kind::PIECES:
223+
new (&template_str) Fmt::Pieces (std::move (other.template_str));
224+
break;
225+
default:
226+
rust_unreachable ();
227+
}
228+
}
229+
230+
FormatArgsPieces &operator= (const FormatArgsPieces &other)
231+
{
232+
this->~FormatArgsPieces ();
233+
new (this) FormatArgsPieces (other);
234+
return *this;
235+
}
236+
237+
FormatArgsPieces &operator= (FormatArgsPieces &&other)
238+
{
239+
this->~FormatArgsPieces ();
240+
new (this) FormatArgsPieces (std::move (other));
241+
return *this;
242+
}
243+
244+
bool has_expr () const { return kind == Kind::EXPR; }
245+
246+
Expr &get_expr ()
247+
{
248+
rust_assert (has_expr ());
249+
return *expr_nl.first;
250+
}
251+
252+
const Expr &get_expr () const
253+
{
254+
rust_assert (has_expr ());
255+
return *expr_nl.first;
256+
}
257+
258+
std::unique_ptr<Expr> &get_expr_ptr ()
259+
{
260+
rust_assert (has_expr ());
261+
return expr_nl.first;
262+
}
263+
264+
bool should_add_newline () const
265+
{
266+
rust_assert (has_expr ());
267+
return expr_nl.second == Newline::Yes;
268+
}
269+
270+
bool has_pieces () const { return kind == Kind::PIECES; }
271+
272+
const Fmt::Pieces &get_pieces () const
273+
{
274+
rust_assert (has_pieces ());
275+
return template_str;
276+
}
277+
278+
void try_simplify ();
279+
280+
Kind get_kind () const { return kind; }
281+
282+
~FormatArgsPieces ()
283+
{
284+
switch (kind)
285+
{
286+
case Kind::EXPR:
287+
expr_nl.~pair ();
288+
break;
289+
case Kind::PIECES:
290+
template_str.~Pieces ();
291+
break;
292+
default:
293+
rust_unreachable ();
294+
}
295+
}
296+
297+
private:
298+
Kind kind;
299+
union
300+
{
301+
std::pair<std::unique_ptr<Expr>, Newline> expr_nl;
302+
Fmt::Pieces template_str;
303+
};
304+
};
305+
171306
// TODO: Format documentation better
172307
// Having a separate AST node for `format_args!()` expansion allows some
173308
// important optimizations which help reduce generated code a lot. For example,
@@ -180,15 +315,11 @@ class FormatArguments
180315
class FormatArgs : public Expr
181316
{
182317
public:
183-
enum class Newline
184-
{
185-
Yes,
186-
No
187-
};
318+
using Newline = FormatArgsNewline;
188319

189-
FormatArgs (location_t loc, Fmt::Pieces &&template_str,
320+
FormatArgs (location_t loc, FormatArgsPieces template_arg,
190321
FormatArguments &&arguments)
191-
: loc (loc), template_pieces (std::move (template_str)),
322+
: loc (loc), template_arg (std::move (template_arg)),
192323
arguments (std::move (arguments))
193324
{}
194325

@@ -198,7 +329,12 @@ class FormatArgs : public Expr
198329

199330
void accept_vis (AST::ASTVisitor &vis) override;
200331

201-
const Fmt::Pieces &get_template () const { return template_pieces; }
332+
FormatArgsPieces &get_template_arg () { return template_arg; }
333+
const Fmt::Pieces &get_template () const
334+
{
335+
rust_assert (template_arg.has_pieces ());
336+
return template_arg.get_pieces ();
337+
}
202338
const FormatArguments &get_arguments () const { return arguments; }
203339
virtual location_t get_locus () const override;
204340

@@ -210,7 +346,7 @@ class FormatArgs : public Expr
210346
// expansion of format_args!(). There is extra handling associated with it.
211347
// we can maybe do that in rust-fmt.cc? in collect_pieces()? like do the
212348
// transformation into something we can handle better
213-
Fmt::Pieces template_pieces;
349+
FormatArgsPieces template_arg;
214350
FormatArguments arguments;
215351

216352
bool marked_for_strip = false;

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

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "rust-path.h"
2828
#include "rust-system.h"
2929
#include "rust-token.h"
30+
#include "rust-expand-visitor.h"
3031

3132
namespace Rust {
3233
namespace Fmt {
@@ -65,10 +66,58 @@ get_trait_name (ffi::FormatSpec format_specifier)
6566
return it->second;
6667
}
6768

68-
tl::optional<AST::Fragment>
69-
expand_format_args (AST::FormatArgs &fmt,
70-
std::vector<std::unique_ptr<AST::Token>> &&tokens)
69+
static bool
70+
expand_format_first_arg (ExpandVisitor &expand_visitor,
71+
AST::FormatArgsPieces &fmt_arg)
7172
{
73+
if (fmt_arg.has_pieces ())
74+
return true;
75+
76+
expand_visitor.maybe_expand_expr (fmt_arg.get_expr_ptr ());
77+
78+
AST::Expr &expr = fmt_arg.get_expr ();
79+
80+
auto emit_err = [&] () {
81+
rust_error_at (expr.get_locus (),
82+
"format argument must be a string literal");
83+
};
84+
85+
if (!expr.is_literal ())
86+
{
87+
if (expr.get_expr_kind () != AST::Expr::Kind::MacroInvocation)
88+
emit_err ();
89+
return false;
90+
}
91+
92+
auto &lit_expr = static_cast<AST::LiteralExpr &> (expr);
93+
94+
if (lit_expr.get_lit_type () != AST::Literal::STRING
95+
&& lit_expr.get_lit_type () != AST::Literal::RAW_STRING)
96+
{
97+
emit_err ();
98+
return false;
99+
}
100+
101+
auto fmt_str = lit_expr.get_literal ().as_string ();
102+
103+
// TODO: does Fmt::Pieces::collect do this?
104+
if (fmt_arg.should_add_newline ())
105+
fmt_str += '\n';
106+
107+
auto pieces = Fmt::Pieces::collect (fmt_str, fmt_arg.should_add_newline (),
108+
Fmt::ffi::ParseMode::Format);
109+
110+
fmt_arg = pieces;
111+
return true;
112+
}
113+
114+
void
115+
expand_format_args (ExpandVisitor &expand_visitor, MacroExpander &expander,
116+
AST::FormatArgs &fmt)
117+
{
118+
if (!expand_format_first_arg (expand_visitor, fmt.get_template_arg ()))
119+
return;
120+
72121
auto loc = fmt.get_locus ();
73122
auto builder = AST::Builder (loc);
74123
auto &arguments = fmt.get_arguments ();
@@ -131,9 +180,12 @@ expand_format_args (AST::FormatArgs &fmt,
131180
auto final_call
132181
= builder.call (std::move (final_path), std::move (final_args));
133182

134-
auto node = AST::SingleASTNode (std::move (final_call));
183+
// TODO: is it alright to have an empty token list?
184+
std::vector<std::unique_ptr<AST::Token>> fragment_tokens;
135185

136-
return AST::Fragment ({node}, std::move (tokens));
186+
expander.set_expanded_fragment (
187+
AST::Fragment ({AST::SingleASTNode (std::move (final_call))},
188+
std::move (fragment_tokens)));
137189
}
138190

139191
} // namespace Fmt

gcc/rust/expand/rust-expand-format-args.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,20 @@
1919
#ifndef RUST_EXPAND_FORMAT_ARGS_H
2020
#define RUST_EXPAND_FORMAT_ARGS_H
2121

22-
#include "optional.h"
22+
#include "rust-system.h"
2323
#include "rust-ast-fragment.h"
24+
#include "optional.h"
2425

2526
namespace Rust {
27+
28+
// forward declare
29+
class ExpandVisitor;
30+
struct MacroExpander;
31+
2632
namespace Fmt {
2733

28-
tl::optional<AST::Fragment>
29-
expand_format_args (AST::FormatArgs &fmt,
30-
std::vector<std::unique_ptr<AST::Token>> &&tokens);
34+
void expand_format_args (ExpandVisitor &expand_visitor, MacroExpander &expander,
35+
AST::FormatArgs &fmt);
3136

3237
} // namespace Fmt
3338
} // namespace Rust

gcc/rust/expand/rust-expand-visitor.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "rust-ast.h"
2424
#include "rust-type.h"
2525
#include "rust-derive.h"
26+
#include "rust-expand-format-args.h"
2627

2728
namespace Rust {
2829

@@ -1061,6 +1062,21 @@ ExpandVisitor::visit (AST::SelfParam &param)
10611062
maybe_expand_type (param.get_type_ptr ());
10621063
}
10631064

1065+
void
1066+
ExpandVisitor::visit (AST::FormatArgs &fmt)
1067+
{
1068+
if (macro_invoc_expect_id != fmt.get_node_id ())
1069+
{
1070+
rust_internal_error_at (fmt.get_locus (),
1071+
"attempting to expand FormatArgs node with id %d "
1072+
"into position with node id %d",
1073+
(int) fmt.get_node_id (),
1074+
(int) macro_invoc_expect_id);
1075+
}
1076+
1077+
Fmt::expand_format_args (*this, expander, fmt);
1078+
}
1079+
10641080
template <typename T>
10651081
void
10661082
ExpandVisitor::expand_inner_attribute (T &item, AST::SimplePath &path)

gcc/rust/expand/rust-expand-visitor.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "rust-ast-visitor.h"
2323
#include "rust-macro-expand.h"
2424
#include "rust-proc-macro.h"
25+
#include "rust-ast-full-decls.h"
2526

2627
namespace Rust {
2728

@@ -284,6 +285,9 @@ class ExpandVisitor : public AST::DefaultASTVisitor
284285
void visit (AST::FunctionParam &type) override;
285286
void visit (AST::SelfParam &type) override;
286287

288+
// expand these if possible
289+
void visit (AST::FormatArgs &fmt) override;
290+
287291
template <typename T>
288292
void expand_inner_attribute (T &item, AST::SimplePath &Path);
289293

0 commit comments

Comments
 (0)