diff --git a/gcc/rust/ast/rust-ast-visitor.cc b/gcc/rust/ast/rust-ast-visitor.cc index ab8cdbe4510e..b9b5e50ca3f0 100644 --- a/gcc/rust/ast/rust-ast-visitor.cc +++ b/gcc/rust/ast/rust-ast-visitor.cc @@ -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 diff --git a/gcc/rust/ast/rust-ast.cc b/gcc/rust/ast/rust-ast.cc index 8e856fb239f5..4dd4a76ab3ba 100644 --- a/gcc/rust/ast/rust-ast.cc +++ b/gcc/rust/ast/rust-ast.cc @@ -5025,8 +5025,6 @@ FormatArgs::set_outer_attrs (std::vector) Expr * FormatArgs::clone_expr_impl () const { - std::cerr << "[ARTHUR] cloning FormatArgs! " << std::endl; - return new FormatArgs (*this); } diff --git a/gcc/rust/ast/rust-builtin-ast-nodes.h b/gcc/rust/ast/rust-builtin-ast-nodes.h index 2893e7b46760..02f753535083 100644 --- a/gcc/rust/ast/rust-builtin-ast-nodes.h +++ b/gcc/rust/ast/rust-builtin-ast-nodes.h @@ -168,6 +168,141 @@ class FormatArguments std::vector 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, 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, 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, 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 &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, 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, @@ -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)) {} @@ -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; @@ -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; diff --git a/gcc/rust/ast/rust-fmt.cc b/gcc/rust/ast/rust-fmt.cc index a29c8203ae8f..21f4f03bb6fe 100644 --- a/gcc/rust/ast/rust-fmt.cc +++ b/gcc/rust/ast/rust-fmt.cc @@ -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 into - // tl::optional? - auto pieces_vector = std::vector (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 ()); + 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 diff --git a/gcc/rust/ast/rust-fmt.h b/gcc/rust/ast/rust-fmt.h index 3722db291ab8..c23499c3709a 100644 --- a/gcc/rust/ast/rust-fmt.h +++ b/gcc/rust/ast/rust-fmt.h @@ -20,20 +20,138 @@ #define RUST_FMT_H #include "rust-system.h" - -// FIXME: How to encode Option? +#include "optional.h" namespace Rust { namespace Fmt { namespace ffi { +extern "C" { + +unsigned char *rust_ffi_alloc (size_t count, size_t elem_size, size_t align); + +void rust_ffi_dealloc (unsigned char *data, size_t count, size_t elem_size, + size_t align); + +} // extern "C" + +template class FFIVec +{ + T *data; + size_t len; + size_t cap; + +public: + FFIVec () : data ((T *) alignof (T)), len (0), cap (0) {} + + FFIVec (const FFIVec &) = delete; + FFIVec &operator= (const FFIVec &) = delete; + + FFIVec (FFIVec &&other) : data (other.data), len (other.len), cap (other.cap) + { + other.data = (T *) alignof (T); + other.len = 0; + other.cap = 0; + } + + FFIVec &operator= (FFIVec &&other) + { + this->~FFIVec (); + new (this) FFIVec (std::move (other)); + return *this; + } + + ~FFIVec () + { + // T can't be zero-sized + if (cap) + rust_ffi_dealloc ((unsigned char *) data, cap, sizeof (T), alignof (T)); + } + + size_t size () const { return len; } + + const T &operator[] (size_t idx) const + { + rust_assert (idx <= len); + return data[idx]; + } + + T *begin () { return data; } + const T *begin () const { return data; } + T *end () { return data + len; } + const T *end () const { return data + len; } +}; + +template class FFIOpt +{ + struct alignas (T) Inner + { + char data[sizeof (T)]; + } inner; + bool is_some; + +public: + template FFIOpt (U &&val) : is_some (true) + { + new (inner.data) T (std::forward (val)); + } + + FFIOpt () : is_some (false) {} + + FFIOpt (const FFIOpt &other) : is_some (other.is_some) + { + if (is_some) + new (inner.data) T (*(const T *) other.inner.data); + } + + FFIOpt (FFIOpt &&other) : is_some (other.is_some) + { + if (is_some) + new (inner.data) T (std::move (*(const T *) other.inner.data)); + } + + ~FFIOpt () + { + if (is_some) + ((T *) inner.data)->~T (); + } + + FFIOpt &operator= (const FFIOpt &other) + { + this->~FFIOpt (); + new (this) FFIOpt (other); + return *this; + } + + FFIOpt &operator= (FFIOpt &&other) + { + this->~FFIOpt (); + new (this) FFIOpt (std::move (other)); + return *this; + } + + tl::optional> get_opt () + { + return (T *) inner.data; + } + + tl::optional> get_opt () const + { + return (const T *) inner.data; + } +}; + struct RustHamster { const char *ptr; size_t len; std::string to_string () const; + + explicit RustHamster (const std::string &str) + : ptr (str.data ()), len (str.size ()) + {} }; /// Enum of alignments which are supported. @@ -166,33 +284,33 @@ struct Count struct FormatSpec { /// Optionally specified character to fill alignment with. - const uint32_t *fill; + FFIOpt fill; /// Span of the optionally specified fill character. - const InnerSpan *fill_span; + FFIOpt fill_span; /// Optionally specified alignment. Alignment align; /// The `+` or `-` flag. - const Sign *sign; + FFIOpt sign; /// The `#` flag. bool alternate; /// The `0` flag. bool zero_pad; /// The `x` or `X` flag. (Only for `Debug`.) - const DebugHex *debug_hex; + FFIOpt debug_hex; /// The integer precision to use. Count precision; /// The span of the precision formatting flag (for diagnostics). - const InnerSpan *precision_span; + FFIOpt precision_span; /// The string width requested for the resulting format. Count width; /// The span of the width formatting flag (for diagnostics). - const InnerSpan *width_span; + FFIOpt width_span; /// The descriptor string representing the name of the format desired for /// this argument, this can be empty or any number of characters, although /// it is required to be one word. RustHamster ty; /// The span of the descriptor string (for diagnostics). - const InnerSpan *ty_span; + FFIOpt ty_span; }; /// Representation of an argument specification. @@ -238,26 +356,6 @@ struct Piece }; }; -struct PieceSlice -{ - const Piece *base_ptr; - size_t len; - size_t cap; -}; - -struct RustString -{ - const unsigned char *ptr; - size_t len; - size_t cap; -}; - -struct FormatArgsHandle -{ - PieceSlice piece_slice; - RustString rust_string; -}; - enum ParseMode { Format = 0, @@ -266,12 +364,10 @@ enum ParseMode extern "C" { -FormatArgsHandle collect_pieces (const char *input, bool append_newline, - ParseMode parse_mode); +FFIVec collect_pieces (RustHamster input, bool append_newline, + ParseMode parse_mode); -FormatArgsHandle clone_pieces (const FormatArgsHandle &); - -void destroy_pieces (FormatArgsHandle); +FFIVec clone_pieces (const FFIVec &); } // extern "C" @@ -281,33 +377,20 @@ struct Pieces { static Pieces collect (const std::string &to_parse, bool append_newline, ffi::ParseMode parse_mode); - ~Pieces (); - - Pieces (const Pieces &other); - Pieces &operator= (const Pieces &other); - Pieces (Pieces &&other); - - const std::vector &get_pieces () const { return pieces_vector; } - - // { - // slice = clone_pieces (&other.slice); - // to_parse = other.to_parse; - - // return *this; - // } + const ffi::FFIVec &get_pieces () const { return data->second; } private: - Pieces (ffi::FormatArgsHandle handle, std::vector &&pieces_vector) - : pieces_vector (std::move (pieces_vector)), handle (handle) + Pieces (std::string str, ffi::FFIVec pieces) + : data ( + std::make_shared (std::move (str), + std::move (pieces))) {} - std::vector pieces_vector; - - // this memory is held for FFI reasons - it needs to be released and cloned - // precisely, so try to not access it/modify it if possible. you should - // instead work with `pieces_vector` - ffi::FormatArgsHandle handle; + // makes copying simpler + // also, we'd need to keep the parsed string in a shared_ptr anyways + // since we store pointers into the parsed string + std::shared_ptr>> data; }; } // namespace Fmt diff --git a/gcc/rust/expand/rust-expand-format-args.cc b/gcc/rust/expand/rust-expand-format-args.cc index bda28dd61ce8..27a9ab700d3e 100644 --- a/gcc/rust/expand/rust-expand-format-args.cc +++ b/gcc/rust/expand/rust-expand-format-args.cc @@ -27,6 +27,7 @@ #include "rust-path.h" #include "rust-system.h" #include "rust-token.h" +#include "rust-expand-visitor.h" namespace Rust { namespace Fmt { @@ -65,10 +66,58 @@ get_trait_name (ffi::FormatSpec format_specifier) return it->second; } -tl::optional -expand_format_args (AST::FormatArgs &fmt, - std::vector> &&tokens) +static bool +expand_format_first_arg (ExpandVisitor &expand_visitor, + AST::FormatArgsPieces &fmt_arg) { + if (fmt_arg.has_pieces ()) + return true; + + expand_visitor.maybe_expand_expr (fmt_arg.get_expr_ptr ()); + + AST::Expr &expr = fmt_arg.get_expr (); + + auto emit_err = [&] () { + rust_error_at (expr.get_locus (), + "format argument must be a string literal"); + }; + + if (!expr.is_literal ()) + { + if (expr.get_expr_kind () != AST::Expr::Kind::MacroInvocation) + emit_err (); + return false; + } + + auto &lit_expr = static_cast (expr); + + if (lit_expr.get_lit_type () != AST::Literal::STRING + && lit_expr.get_lit_type () != AST::Literal::RAW_STRING) + { + emit_err (); + return false; + } + + auto fmt_str = lit_expr.get_literal ().as_string (); + + // TODO: does Fmt::Pieces::collect do this? + if (fmt_arg.should_add_newline ()) + fmt_str += '\n'; + + auto pieces = Fmt::Pieces::collect (fmt_str, fmt_arg.should_add_newline (), + Fmt::ffi::ParseMode::Format); + + fmt_arg = pieces; + return true; +} + +void +expand_format_args (ExpandVisitor &expand_visitor, MacroExpander &expander, + AST::FormatArgs &fmt) +{ + if (!expand_format_first_arg (expand_visitor, fmt.get_template_arg ())) + return; + auto loc = fmt.get_locus (); auto builder = AST::Builder (loc); auto &arguments = fmt.get_arguments (); @@ -131,9 +180,12 @@ expand_format_args (AST::FormatArgs &fmt, auto final_call = builder.call (std::move (final_path), std::move (final_args)); - auto node = AST::SingleASTNode (std::move (final_call)); + // TODO: is it alright to have an empty token list? + std::vector> fragment_tokens; - return AST::Fragment ({node}, std::move (tokens)); + expander.set_expanded_fragment ( + AST::Fragment ({AST::SingleASTNode (std::move (final_call))}, + std::move (fragment_tokens))); } } // namespace Fmt diff --git a/gcc/rust/expand/rust-expand-format-args.h b/gcc/rust/expand/rust-expand-format-args.h index 2692ef9a404e..76165cd6b61f 100644 --- a/gcc/rust/expand/rust-expand-format-args.h +++ b/gcc/rust/expand/rust-expand-format-args.h @@ -19,15 +19,20 @@ #ifndef RUST_EXPAND_FORMAT_ARGS_H #define RUST_EXPAND_FORMAT_ARGS_H -#include "optional.h" +#include "rust-system.h" #include "rust-ast-fragment.h" +#include "optional.h" namespace Rust { + +// forward declare +class ExpandVisitor; +struct MacroExpander; + namespace Fmt { -tl::optional -expand_format_args (AST::FormatArgs &fmt, - std::vector> &&tokens); +void expand_format_args (ExpandVisitor &expand_visitor, MacroExpander &expander, + AST::FormatArgs &fmt); } // namespace Fmt } // namespace Rust diff --git a/gcc/rust/expand/rust-expand-visitor.cc b/gcc/rust/expand/rust-expand-visitor.cc index a53f0640109d..d11bbe5032bb 100644 --- a/gcc/rust/expand/rust-expand-visitor.cc +++ b/gcc/rust/expand/rust-expand-visitor.cc @@ -23,6 +23,7 @@ #include "rust-ast.h" #include "rust-type.h" #include "rust-derive.h" +#include "rust-expand-format-args.h" namespace Rust { @@ -1061,6 +1062,21 @@ ExpandVisitor::visit (AST::SelfParam ¶m) maybe_expand_type (param.get_type_ptr ()); } +void +ExpandVisitor::visit (AST::FormatArgs &fmt) +{ + if (macro_invoc_expect_id != fmt.get_node_id ()) + { + rust_internal_error_at (fmt.get_locus (), + "attempting to expand FormatArgs node with id %d " + "into position with node id %d", + (int) fmt.get_node_id (), + (int) macro_invoc_expect_id); + } + + Fmt::expand_format_args (*this, expander, fmt); +} + template void ExpandVisitor::expand_inner_attribute (T &item, AST::SimplePath &path) diff --git a/gcc/rust/expand/rust-expand-visitor.h b/gcc/rust/expand/rust-expand-visitor.h index 01a0d1cb2975..81c7f52438ec 100644 --- a/gcc/rust/expand/rust-expand-visitor.h +++ b/gcc/rust/expand/rust-expand-visitor.h @@ -22,6 +22,7 @@ #include "rust-ast-visitor.h" #include "rust-macro-expand.h" #include "rust-proc-macro.h" +#include "rust-ast-full-decls.h" namespace Rust { @@ -284,6 +285,9 @@ class ExpandVisitor : public AST::DefaultASTVisitor void visit (AST::FunctionParam &type) override; void visit (AST::SelfParam &type) override; + // expand these if possible + void visit (AST::FormatArgs &fmt) override; + template void expand_inner_attribute (T &item, AST::SimplePath &Path); diff --git a/gcc/rust/expand/rust-macro-builtins-asm.cc b/gcc/rust/expand/rust-macro-builtins-asm.cc index f9d2947163d1..14df08297488 100644 --- a/gcc/rust/expand/rust-macro-builtins-asm.cc +++ b/gcc/rust/expand/rust-macro-builtins-asm.cc @@ -787,12 +787,12 @@ expand_inline_asm_strings (InlineAsmContext inline_asm_ctx) auto pieces = Fmt::Pieces::collect (template_str.symbol, false, Fmt::ffi::ParseMode::InlineAsm); - auto pieces_vec = pieces.get_pieces (); + auto &pieces_vec = pieces.get_pieces (); std::string transformed_template_str = ""; for (size_t i = 0; i < pieces_vec.size (); i++) { - auto piece = pieces_vec[i]; + auto &piece = pieces_vec[i]; if (piece.tag == Fmt::ffi::Piece::Tag::String) { transformed_template_str += piece.string._0.to_string (); diff --git a/gcc/rust/expand/rust-macro-builtins-format-args.cc b/gcc/rust/expand/rust-macro-builtins-format-args.cc index b20c84953206..3671c21e1087 100644 --- a/gcc/rust/expand/rust-macro-builtins-format-args.cc +++ b/gcc/rust/expand/rust-macro-builtins-format-args.cc @@ -24,7 +24,7 @@ namespace Rust { struct FormatArgsInput { - std::string format_str; + std::unique_ptr format_expr; AST::FormatArguments args; // bool is_literal? }; @@ -48,28 +48,10 @@ format_args_parse_arguments (AST::MacroInvocData &invoc) auto args = AST::FormatArguments (); auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser); - std::unique_ptr format_expr = nullptr; - - // TODO: Handle the case where we're not parsing a string literal (macro - // invocation for e.g.) - switch (parser.peek_current_token ()->get_id ()) - { - case STRING_LITERAL: - case RAW_STRING_LITERAL: - format_expr = parser.parse_literal_expr (); - default: - // do nothing - ; - } + auto format_expr = parser.parse_expr (); rust_assert (format_expr); - // TODO(Arthur): Clean this up - if we haven't parsed a string literal but a - // macro invocation, what do we do here? return a tl::unexpected? - auto format_str = static_cast (*format_expr) - .get_literal () - .as_string (); - // TODO: Allow implicit captures ONLY if the the first arg is a string literal // and not a macro invocation @@ -126,7 +108,7 @@ format_args_parse_arguments (AST::MacroInvocData &invoc) // we need to skip commas, don't we? } - return FormatArgsInput{std::move (format_str), std::move (args)}; + return FormatArgsInput{std::move (format_expr), std::move (args)}; } tl::optional @@ -144,67 +126,12 @@ MacroBuiltin::format_args_handler (location_t invoc_locus, return tl::nullopt; } - // TODO(Arthur): We need to handle this - // // if it is not a literal, it's an eager macro invocation - return it - // if (!fmt_expr->is_literal ()) - // { - // auto token_tree = invoc.get_delim_tok_tree (); - // return AST::Fragment ({AST::SingleASTNode (std::move (fmt_expr))}, - // token_tree.to_token_stream ()); - // } - - // TODO(Arthur): Handle this as well - raw strings are special for the - // format_args parser auto fmt_str = static_cast - // (*fmt_arg.get ()); Switch on the format string to know if the string is raw - // or cooked switch (fmt_str.get_lit_type ()) - // { - // // case AST::Literal::RAW_STRING: - // case AST::Literal::STRING: - // break; - // case AST::Literal::CHAR: - // case AST::Literal::BYTE: - // case AST::Literal::BYTE_STRING: - // case AST::Literal::INT: - // case AST::Literal::FLOAT: - // case AST::Literal::BOOL: - // case AST::Literal::ERROR: - // rust_unreachable (); - // } - - bool append_newline = nl == AST::FormatArgs::Newline::Yes; - - auto fmt_str = std::move (input->format_str); - if (append_newline) - fmt_str += '\n'; - - auto pieces = Fmt::Pieces::collect (fmt_str, append_newline, - Fmt::ffi::ParseMode::Format); - - // TODO: - // do the transformation into an AST::FormatArgs node - // return that - // expand it during lowering - - // TODO: we now need to take care of creating `unfinished_literal`? this is - // for creating the `template` - - auto fmt_args_node = AST::FormatArgs (invoc_locus, std::move (pieces), - std::move (input->args)); - - auto expanded - = Fmt::expand_format_args (fmt_args_node, - invoc.get_delim_tok_tree ().to_token_stream ()); - - if (!expanded.has_value ()) - return AST::Fragment::create_error (); - - return *expanded; - - // auto node = std::unique_ptr (fmt_args_node); - // auto single_node = AST::SingleASTNode (std::move (node)); - - // return AST::Fragment ({std::move (single_node)}, - // invoc.get_delim_tok_tree ().to_token_stream ()); + auto node = AST::SingleASTNode (std::make_unique ( + invoc_locus, AST::FormatArgsPieces (std::move (input->format_expr), nl), + std::move (input->args))); + + return AST::Fragment ({std::move (node)}, + invoc.get_delim_tok_tree ().to_token_stream ()); } } // namespace Rust diff --git a/gcc/testsuite/rust/compile/format_args_concat.rs b/gcc/testsuite/rust/compile/format_args_concat.rs new file mode 100644 index 000000000000..b180667e9e6f --- /dev/null +++ b/gcc/testsuite/rust/compile/format_args_concat.rs @@ -0,0 +1,51 @@ +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +macro_rules! format_args { + () => {}; +} + +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} + +#[lang = "sized"] +trait Sized {} + +pub mod core { + pub mod fmt { + pub struct Formatter; + pub struct Result; + + pub struct Arguments<'a>; + + impl<'a> Arguments<'a> { + pub fn new_v1(_: &'a [&'static str], _: &'a [ArgumentV1<'a>]) -> Arguments<'a> { + Arguments + } + } + + pub struct ArgumentV1<'a>; + + impl<'a> ArgumentV1<'a> { + pub fn new<'b, T>(_: &'b T, _: fn(&T, &mut Formatter) -> Result) -> ArgumentV1 { + ArgumentV1 + } + } + + pub trait Display { + fn fmt(&self, _: &mut Formatter) -> Result; + } + + impl Display for i32 { + fn fmt(&self, _: &mut Formatter) -> Result { + Result + } + } + } +} + +fn main() { + let _formatted = format_args!(concat!("hello ", "{}"), 15); +} diff --git a/libgrust/libformat_parser/src/lib.rs b/libgrust/libformat_parser/src/lib.rs index 72e5971ae805..049403db0b7f 100644 --- a/libgrust/libformat_parser/src/lib.rs +++ b/libgrust/libformat_parser/src/lib.rs @@ -1,56 +1,197 @@ //! FFI interface for `rustc_format_parser` +use std::alloc::Layout; + // what's the plan? Have a function return something that can be constructed into a vector? // or an iterator? -use std::ffi::CStr; - trait IntoFFI { fn into_ffi(self) -> T; } -impl IntoFFI<*const T> for Option -where - T: Sized, -{ - fn into_ffi(self) -> *const T { - match self.as_ref() { - None => std::ptr::null(), - Some(r) => r as *const T, +// FIXME: Make an ffi module in a separate file +// FIXME: Remember to leak the boxed type somehow +// FIXME: How to encode the Option type? As a pointer? Option -> Option<&T> -> *const T could work maybe? +pub mod ffi { + use super::IntoFFI; + use std::marker::PhantomData; + use std::mem::MaybeUninit; + + #[repr(C)] + pub struct FFIVec { + data: *mut T, + len: usize, + cap: usize + } + + impl IntoFFI> for Vec { + fn into_ffi(mut self) -> FFIVec { + let ret = FFIVec { + data: self.as_mut_ptr(), + len: self.len(), + cap: self.capacity() + }; + self.leak(); + ret } } -} -// Extension trait to provide `String::leak` which did not exist in Rust 1.49 -pub trait StringLeakExt { - fn leak<'a>(self) -> &'a mut str; -} + impl Drop for FFIVec { + fn drop(&mut self) { + unsafe { + Vec::from_raw_parts(self.data, self.len, self.cap); + } + } + } -impl StringLeakExt for String { - fn leak<'a>(self) -> &'a mut str { - Box::leak(self.into_boxed_str()) + impl FFIVec { + fn with_vec_ref FnOnce(&'a Vec) -> R>( + &self, f: F + ) -> R { + let v = unsafe { + Vec::from_raw_parts(self.data, self.len, self.cap) + }; + let ret = f(&v); + v.leak(); + ret + } + + // currently unused + // may be nice to have later, though + #[allow(unused)] + fn with_vec_mut_ref FnOnce(&'a mut Vec) -> R>( + &mut self, f: F + ) -> R { + let mut v = unsafe { + Vec::from_raw_parts(self.data, self.len, self.cap) + }; + let ret = f(&mut v); + self.data = v.as_mut_ptr(); + self.len = v.len(); + self.cap = v.capacity(); + v.leak(); + ret + } } -} -// FIXME: Make an ffi module in a separate file -// FIXME: Remember to leak the boxed type somehow -// FIXME: How to encode the Option type? As a pointer? Option -> Option<&T> -> *const T could work maybe? -pub mod ffi { - use super::IntoFFI; + impl Clone for FFIVec + where + T: Clone + { + fn clone(&self) -> FFIVec { + self.with_vec_ref(|v| v.clone().into_ffi()) + } + } + + #[repr(C)] + pub struct FFIOpt { + val: MaybeUninit, + is_some: bool + } + + impl Clone for FFIOpt + where + T: Clone + { + fn clone(&self) -> Self { + match self.get_opt_ref() { + Some(r) => FFIOpt::new_val(r.clone()), + None => FFIOpt::new_none() + } + } + } + + impl PartialEq for FFIOpt + where + T: PartialEq + { + fn eq(&self, other: &Self) -> bool { + match (self.get_opt_ref(), other.get_opt_ref()) { + (Some(a), Some(b)) => a.eq(b), + _ => false + } + } + } + + impl Eq for FFIOpt + where + T: Eq + {} + + impl Drop for FFIOpt + { + fn drop(&mut self) { + if self.is_some { + unsafe { std::ptr::drop_in_place(self.val.as_mut_ptr()) } + } + } + } + + impl IntoFFI> for Option { + fn into_ffi(self) -> FFIOpt { + match self { + Some(v) => FFIOpt::new_val(v), + None => FFIOpt::new_none() + } + } + } + + impl FFIOpt { + pub fn new_val(v: T) -> Self { + FFIOpt { + val: MaybeUninit::new(v), + is_some: true + } + } + + pub fn new_none() -> Self { + FFIOpt { + val: MaybeUninit::uninit(), + is_some: false + } + } + + pub fn get_opt_ref(&self) -> Option<&T> { + if self.is_some { + Some(unsafe {&*self.val.as_ptr()}) + } else { + None + } + } + + pub fn get_opt_ref_mut(&mut self) -> Option<&mut T> { + if self.is_some { + Some(unsafe {&mut *self.val.as_mut_ptr()}) + } else { + None + } + } + } // FIXME: We need to ensure we deal with memory properly - whether it's owned by the C++ side or the Rust side #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[repr(C)] - pub struct RustHamster { + pub struct RustHamster<'a> { ptr: *const u8, len: usize, + phantom: PhantomData<&'a u8> } - impl<'a> From<&'a str> for RustHamster { - fn from(s: &'a str) -> RustHamster { + impl<'a> IntoFFI> for &'a str { + fn into_ffi(self) -> RustHamster<'a> { RustHamster { - ptr: s.as_ptr(), - len: s.len(), + ptr: self.as_ptr(), + len: self.len(), + phantom: PhantomData, + } + } + } + + impl<'a> RustHamster<'a> { + pub fn as_str(&self) -> &'a str { + unsafe { + let slice: &'a [u8] = std::slice::from_raw_parts(self.ptr, self.len); + std::str::from_utf8_unchecked(slice) } } } @@ -101,11 +242,11 @@ pub mod ffi { /// A piece is a portion of the format string which represents the next part /// to emit. These are emitted as a stream by the `Parser` class. - #[derive(Debug, Clone, PartialEq)] + #[derive(Clone)] #[repr(C)] pub enum Piece<'a> { /// A literal string which should directly be emitted - String(RustHamster), + String(RustHamster<'a>), /// This describes that formatting should process the next argument (as /// specified inside) for emission. // do we need a pointer here? we're doing big cloning anyway @@ -113,7 +254,7 @@ pub mod ffi { } /// Representation of an argument specification. - #[derive(Copy, Clone, Debug, PartialEq)] + #[derive(Clone)] #[repr(C)] pub struct Argument<'a> { /// Where to find this argument @@ -126,37 +267,37 @@ pub mod ffi { } /// Specification for the formatting of an argument in the format string. - #[derive(Copy, Clone, Debug, PartialEq)] + #[derive(Clone)] #[repr(C)] pub struct FormatSpec<'a> { /// Optionally specified character to fill alignment with. - pub fill: Option, + pub fill: FFIOpt, /// Span of the optionally specified fill character. - pub fill_span: *const InnerSpan, + pub fill_span: FFIOpt, /// Optionally specified alignment. pub align: Alignment, /// The `+` or `-` flag. - pub sign: *const Sign, + pub sign: FFIOpt, /// The `#` flag. pub alternate: bool, /// The `0` flag. pub zero_pad: bool, /// The `x` or `X` flag. (Only for `Debug`.) - pub debug_hex: *const DebugHex, + pub debug_hex: FFIOpt, /// The integer precision to use. pub precision: Count<'a>, /// The span of the precision formatting flag (for diagnostics). - pub precision_span: *const InnerSpan, + pub precision_span: FFIOpt, /// The string width requested for the resulting format. pub width: Count<'a>, /// The span of the width formatting flag (for diagnostics). - pub width_span: *const InnerSpan, + pub width_span: FFIOpt, /// The descriptor string representing the name of the format desired for /// this argument, this can be empty or any number of characters, although /// it is required to be one word. - pub ty: &'a str, + pub ty: RustHamster<'a>, /// The span of the descriptor string (for diagnostics). - pub ty_span: *const InnerSpan, + pub ty_span: FFIOpt, } /// Enum describing where an argument for a format can be located. @@ -168,7 +309,7 @@ pub mod ffi { /// The argument is located at a specific index given in the format, ArgumentIs(usize), /// The argument has a name. - ArgumentNamed(&'a str), + ArgumentNamed(RustHamster<'a>), } /// Enum of alignments which are supported. @@ -213,7 +354,7 @@ pub mod ffi { /// The count is specified explicitly. CountIs(usize), /// The count is specified by the argument with the given name. - CountIsName(&'a str, InnerSpan), + CountIsName(RustHamster<'a>, InnerSpan), /// The count is specified by the argument at the given index. CountIsParam(usize), /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index. @@ -225,7 +366,7 @@ pub mod ffi { impl<'a> From> for Piece<'a> { fn from(old: generic_format_parser::Piece<'a>) -> Self { match old { - generic_format_parser::Piece::String(x) => Piece::String(x.into()), + generic_format_parser::Piece::String(x) => Piece::String(x.into_ffi()), generic_format_parser::Piece::NextArgument(x) => { // FIXME: This is problematic - if we do this, then we probably run into the issue that the Box // is freed at the end of the call to collect_pieces. if we just .leak() it, then we have @@ -259,7 +400,7 @@ pub mod ffi { } generic_format_parser::Position::ArgumentIs(x) => Position::ArgumentIs(x.into()), generic_format_parser::Position::ArgumentNamed(x) => { - Position::ArgumentNamed(x.into()) + Position::ArgumentNamed(x.into_ffi()) } } } @@ -277,7 +418,7 @@ pub mod ffi { impl<'a> From> for FormatSpec<'a> { fn from(old: generic_format_parser::FormatSpec<'a>) -> Self { FormatSpec { - fill: old.fill, + fill: old.fill.into_ffi(), fill_span: old.fill_span.map(Into::into).into_ffi(), align: old.align.into(), sign: old.sign.map(Into::into).into_ffi(), @@ -288,7 +429,7 @@ pub mod ffi { precision_span: old.precision_span.map(Into::into).into_ffi(), width: old.width.into(), width_span: old.width_span.map(Into::into).into_ffi(), - ty: old.ty, + ty: old.ty.into_ffi(), ty_span: old.ty_span.map(Into::into).into_ffi(), } } @@ -307,7 +448,7 @@ pub mod ffi { fn from(old: generic_format_parser::Count<'a>) -> Self { match old { generic_format_parser::Count::CountIs(x) => Count::CountIs(x), - generic_format_parser::Count::CountIsName(x, y) => Count::CountIsName(x, y.into()), + generic_format_parser::Count::CountIsName(x, y) => Count::CountIsName(x.into_ffi(), y.into()), generic_format_parser::Count::CountIsParam(x) => Count::CountIsParam(x), generic_format_parser::Count::CountIsStar(x) => Count::CountIsStar(x), generic_format_parser::Count::CountImplied => Count::CountImplied, @@ -357,100 +498,66 @@ pub mod rust { } // TODO: Should we instead make an FFIVector struct? -#[repr(C)] -pub struct PieceSlice { - base_ptr: *mut ffi::Piece<'static /* FIXME: That's wrong */>, - len: usize, - cap: usize, -} - -#[repr(C)] -// FIXME: we should probably use FFIString here -pub struct RustString { - ptr: *const u8, - len: usize, - cap: usize, -} - -#[repr(C)] -pub struct FormatArgsHandle(PieceSlice, RustString); +type PieceVec<'a> = ffi::FFIVec>; #[no_mangle] -pub extern "C" fn collect_pieces( - input: *const libc::c_char, +pub extern "C" fn collect_pieces<'a>( + input: ffi::RustHamster<'a>, append_newline: bool, parse_mode: crate::ffi::ParseMode, -) -> FormatArgsHandle { - // FIXME: Add comment - let str = unsafe { CStr::from_ptr(input) }; - let str = str.to_str().unwrap().to_owned(); - - // we are never going to free this string here (we leak it later on), so we can extend its lifetime - // to send it across an FFI boundary. - // FIXME: Is that correct? - let s = &str; - let s = unsafe { std::mem::transmute::<&'_ str, &'static str>(s) }; - +) -> PieceVec<'a> { // FIXME: No unwrap let pieces: Vec> = - rust::collect_pieces(s, None, None, append_newline, parse_mode) + rust::collect_pieces(input.as_str(), None, None, append_newline, parse_mode) .into_iter() .map(Into::into) .collect(); - let piece_slice = PieceSlice { - len: pieces.len(), - cap: pieces.capacity(), - base_ptr: pieces.leak().as_mut_ptr(), - }; - let rust_string = RustString { - len: str.len(), - cap: str.capacity(), - ptr: str.leak().as_ptr(), - }; - - FormatArgsHandle(piece_slice, rust_string) + pieces.into_ffi() } #[no_mangle] -pub unsafe extern "C" fn destroy_pieces(FormatArgsHandle(piece_slice, s): FormatArgsHandle) { - let PieceSlice { base_ptr, len, cap } = piece_slice; - drop(Vec::from_raw_parts(base_ptr, len, cap)); - - let RustString { ptr, len, cap } = s; - drop(String::from_raw_parts(ptr as *mut u8, len, cap)); +pub extern "C" fn clone_pieces<'a, 'b>( + piece_vec: &'a PieceVec<'b> +) -> PieceVec<'b> { + piece_vec.clone() } -#[no_mangle] -pub extern "C" fn clone_pieces( - FormatArgsHandle(piece_slice, s): &FormatArgsHandle, -) -> FormatArgsHandle { - let PieceSlice { base_ptr, len, cap } = *piece_slice; - - let v = unsafe { Vec::from_raw_parts(base_ptr, len, cap) }; - let cloned_v = v.clone(); - - // FIXME: Add documentation - v.leak(); - - let piece_slice = PieceSlice { - len: cloned_v.len(), - cap: cloned_v.capacity(), - base_ptr: cloned_v.leak().as_mut_ptr(), - }; - - let RustString { ptr, len, cap } = *s; - let s = unsafe { String::from_raw_parts(ptr as *mut u8, len, cap) }; - let cloned_s = s.clone(); +// we need Layout::repeat +// function signature is a bit different, so call it repeat_x +trait LayoutExt { + fn repeat_x(&self, n: usize) -> Layout; +} - // FIXME: Documentation - s.leak(); +impl LayoutExt for Layout { + fn repeat_x(&self, n: usize) -> Layout { + let elem = self.pad_to_align(); + let total_size = elem.size().checked_mul(n).unwrap(); + Layout::from_size_align(total_size, elem.align()).unwrap() + } +} - let rust_string = RustString { - len: cloned_s.len(), - cap: cloned_s.capacity(), - ptr: cloned_s.leak().as_ptr(), - }; +#[no_mangle] +pub unsafe extern "C" fn rust_ffi_alloc( + count: usize, elem_size: usize, align: usize +) -> *mut u8 { + unsafe { + std::alloc::alloc( + Layout::from_size_align_unchecked(elem_size, align) + .repeat_x(count) + ) + } +} - FormatArgsHandle(piece_slice, rust_string) +#[no_mangle] +pub unsafe extern "C" fn rust_ffi_dealloc( + data: *mut u8, count: usize, elem_size: usize, align: usize +) { + unsafe { + std::alloc::dealloc( + data, + Layout::from_size_align_unchecked(elem_size, align) + .repeat_x(count) + ) + } }