Skip to content

Allow format_args! with a macro as first argument #4064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion gcc/rust/ast/rust-ast-visitor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1500,9 +1500,12 @@ DefaultASTVisitor::visit (AST::BareFunctionType &type)
}

void
DefaultASTVisitor::visit (AST::FormatArgs &)
DefaultASTVisitor::visit (AST::FormatArgs &fmt)
{
// FIXME: Do we have anything to do? any subnodes to visit? Probably, right?
auto &fmt_arg = fmt.get_template_arg ();
if (fmt_arg.has_expr ())
visit (fmt_arg.get_expr ());
}

void
Expand Down
2 changes: 0 additions & 2 deletions gcc/rust/ast/rust-ast.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5025,8 +5025,6 @@ FormatArgs::set_outer_attrs (std::vector<Attribute>)
Expr *
FormatArgs::clone_expr_impl () const
{
std::cerr << "[ARTHUR] cloning FormatArgs! " << std::endl;

return new FormatArgs (*this);
}

Expand Down
154 changes: 145 additions & 9 deletions gcc/rust/ast/rust-builtin-ast-nodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,141 @@ class FormatArguments
std::vector<FormatArgument> args;
};

enum class FormatArgsNewline
{
Yes,
No
};

// can be either an expression or Fmt::Pieces
// used to handle `format_args!(some_macro!(...), ...)`
class FormatArgsPieces final
{
public:
using Newline = FormatArgsNewline;

enum class Kind
{
EXPR,
PIECES
};

FormatArgsPieces (std::unique_ptr<Expr> expr, Newline nl)
: kind (Kind::EXPR), expr_nl (std::move (expr), nl)
{}

FormatArgsPieces (Fmt::Pieces template_str)
: kind (Kind::PIECES), template_str (std::move (template_str))
{}

FormatArgsPieces (const FormatArgsPieces &other) noexcept : kind (other.kind)
{
switch (kind)
{
case Kind::EXPR:
new (&expr_nl) std::pair<std::unique_ptr<Expr>, Newline> (
other.expr_nl.first->clone_expr (), other.expr_nl.second);
break;
case Kind::PIECES:
new (&template_str) Fmt::Pieces (other.template_str);
break;
default:
rust_unreachable ();
}
}

FormatArgsPieces (FormatArgsPieces &&other) noexcept : kind (other.kind)
{
switch (kind)
{
case Kind::EXPR:
new (&expr_nl) std::pair<std::unique_ptr<Expr>, Newline> (
std::move (other.expr_nl.first), other.expr_nl.second);
break;
case Kind::PIECES:
new (&template_str) Fmt::Pieces (std::move (other.template_str));
break;
default:
rust_unreachable ();
}
}

FormatArgsPieces &operator= (const FormatArgsPieces &other)
{
this->~FormatArgsPieces ();
new (this) FormatArgsPieces (other);
return *this;
}

FormatArgsPieces &operator= (FormatArgsPieces &&other)
{
this->~FormatArgsPieces ();
new (this) FormatArgsPieces (std::move (other));
return *this;
}

bool has_expr () const { return kind == Kind::EXPR; }

Expr &get_expr ()
{
rust_assert (has_expr ());
return *expr_nl.first;
}

const Expr &get_expr () const
{
rust_assert (has_expr ());
return *expr_nl.first;
}

std::unique_ptr<Expr> &get_expr_ptr ()
{
rust_assert (has_expr ());
return expr_nl.first;
}

bool should_add_newline () const
{
rust_assert (has_expr ());
return expr_nl.second == Newline::Yes;
}

bool has_pieces () const { return kind == Kind::PIECES; }

const Fmt::Pieces &get_pieces () const
{
rust_assert (has_pieces ());
return template_str;
}

void try_simplify ();

Kind get_kind () const { return kind; }

~FormatArgsPieces ()
{
switch (kind)
{
case Kind::EXPR:
expr_nl.~pair ();
break;
case Kind::PIECES:
template_str.~Pieces ();
break;
default:
rust_unreachable ();
}
}

private:
Kind kind;
union
{
std::pair<std::unique_ptr<Expr>, Newline> expr_nl;
Fmt::Pieces template_str;
};
};

// TODO: Format documentation better
// Having a separate AST node for `format_args!()` expansion allows some
// important optimizations which help reduce generated code a lot. For example,
Expand All @@ -180,15 +315,11 @@ class FormatArguments
class FormatArgs : public Expr
{
public:
enum class Newline
{
Yes,
No
};
using Newline = FormatArgsNewline;

FormatArgs (location_t loc, Fmt::Pieces &&template_str,
FormatArgs (location_t loc, FormatArgsPieces template_arg,
FormatArguments &&arguments)
: loc (loc), template_pieces (std::move (template_str)),
: loc (loc), template_arg (std::move (template_arg)),
arguments (std::move (arguments))
{}

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

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

const Fmt::Pieces &get_template () const { return template_pieces; }
FormatArgsPieces &get_template_arg () { return template_arg; }
const Fmt::Pieces &get_template () const
{
rust_assert (template_arg.has_pieces ());
return template_arg.get_pieces ();
}
const FormatArguments &get_arguments () const { return arguments; }
virtual location_t get_locus () const override;

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

bool marked_for_strip = false;
Expand Down
38 changes: 4 additions & 34 deletions gcc/rust/ast/rust-fmt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,41 +32,11 @@ Pieces
Pieces::collect (const std::string &to_parse, bool append_newline,
ffi::ParseMode parse_mode)
{
auto handle
= ffi::collect_pieces (to_parse.c_str (), append_newline, parse_mode);

// this performs multiple copies, can we avoid them maybe?
// TODO: Instead of just creating a vec of, basically, `ffi::Piece`s, we
// should transform them into the proper C++ type which we can work with. so
// transform all the strings into C++ strings? all the Option<T> into
// tl::optional<T>?
auto pieces_vector = std::vector<ffi::Piece> (handle.piece_slice.base_ptr,
handle.piece_slice.base_ptr
+ handle.piece_slice.len);

return Pieces (handle, std::move (pieces_vector));
}

Pieces::~Pieces () { ffi::destroy_pieces (handle); }

Pieces::Pieces (const Pieces &other) : pieces_vector (other.pieces_vector)
{
handle = ffi::clone_pieces (other.handle);
Pieces ret (to_parse, ffi::FFIVec<ffi::Piece> ());
ret.data->second = ffi::collect_pieces (ffi::RustHamster (ret.data->first),
append_newline, parse_mode);
return ret;
}

Pieces &
Pieces::operator= (const Pieces &other)
{
handle = ffi::clone_pieces (other.handle);
pieces_vector = other.pieces_vector;

return *this;
}

Pieces::Pieces (Pieces &&other)
: pieces_vector (std::move (other.pieces_vector)),
handle (clone_pieces (other.handle))
{}

} // namespace Fmt
} // namespace Rust
Loading
Loading