rust/rust-derive-copy.o \
rust/rust-proc-macro.o \
rust/rust-macro-invoc-lexer.o \
+ rust/rust-proc-macro-invoc-lexer.o \
rust/rust-macro-substitute-ctx.o \
rust/rust-macro-builtins.o \
rust/rust-hir.o \
void visit (EnumItemTuple &item);
void visit (EnumItemStruct &item);
void visit (EnumItemDiscriminant &item);
- void visit (Enum &enum_item);
+ void visit (Enum &enumeration);
void visit (Union &union_item);
void visit (ConstantItem &const_item);
void visit (StaticItem &static_item);
Fragment::Fragment (FragmentKind kind, std::vector<SingleASTNode> nodes,
std::vector<std::unique_ptr<AST::Token>> tokens)
- : kind (kind), nodes (std::move (nodes)), tokens (std::move (tokens))
+ : kind (kind), nodes (std::move (nodes)), tokens (std::move (tokens)),
+ overwrite (true)
{}
Fragment::Fragment (Fragment const &other) : kind (other.get_kind ())
for (auto &t : other.tokens)
tokens.emplace_back (t->clone_token ());
+ overwrite = other.overwrite;
+
return *this;
}
}
Fragment::Fragment (std::vector<AST::SingleASTNode> nodes,
- std::vector<std::unique_ptr<AST::Token>> tokens)
+ std::vector<std::unique_ptr<AST::Token>> tokens,
+ bool overwrite)
: kind (FragmentKind::Complete), nodes (std::move (nodes)),
- tokens (std::move (tokens))
+ tokens (std::move (tokens)), overwrite (overwrite)
{}
Fragment::Fragment (std::vector<AST::SingleASTNode> nodes,
std::unique_ptr<AST::Token> token)
- : kind (FragmentKind::Complete), nodes (std::move (nodes))
+ : kind (FragmentKind::Complete), nodes (std::move (nodes)), overwrite (true)
{
tokens.emplace_back (std::move (token));
}
return !is_error ();
}
+bool
+Fragment::should_overwrite () const
+{
+ return overwrite;
+}
+
bool
Fragment::is_expression_fragment () const
{
* Create a complete AST fragment
*/
Fragment (std::vector<AST::SingleASTNode> nodes,
- std::vector<std::unique_ptr<AST::Token>> tokens);
+ std::vector<std::unique_ptr<AST::Token>> tokens,
+ bool overwrite = true);
/**
* Create a complete AST fragment made of a single token
bool is_error () const;
bool should_expand () const;
+ bool should_overwrite () const;
bool is_expression_fragment () const;
bool is_type_fragment () const;
*/
std::vector<std::unique_ptr<AST::Token>> tokens;
+ /**
+ * Whether the fragment should overwrite the original content. In most case
+ * it should overwrite it, but not with derive procedural macros.
+ */
+ bool overwrite;
+
/**
* We need to make a special case for Expression and Type fragments as only
* one Node will be extracted from the `nodes` vector
ExpandVisitor::expand_outer_attribute (T &item, AST::SimplePath &path)
{
// FIXME: Retrieve path from segments + local use statements instead of string
- proc_expander.expand_attribute_proc_macro (item, path);
+ expander.expand_attribute_proc_macro (item, path);
}
template <typename T>
ExpandVisitor::expand_inner_attribute (T &item, AST::SimplePath &path)
{
// FIXME: Retrieve path from segments + local use statements instead of string
- proc_expander.expand_attribute_proc_macro (item, path);
+ expander.expand_attribute_proc_macro (item, path);
}
template <typename T>
template <typename T>
void
-ExpandVisitor::expand_derive (const T &item,
- std::unique_ptr<AST::TokenTree> &trait)
+ExpandVisitor::expand_derive (T &item, std::unique_ptr<AST::TokenTree> trait)
{
- // FIXME: Implement expansion for that particular trait
+ auto trait_name = trait->as_string ();
+ expander.expand_derive_proc_macro (item, trait_name);
}
template <typename T>
void
-ExpandVisitor::expand_derive (const T &item, AST::DelimTokenTree &attr)
+ExpandVisitor::expand_derive (T &item, AST::DelimTokenTree &attr)
{
// Item is const because even though the tokenstream might be modified, it
// should appear as the same input for every derive proc macro.
for (auto it = trees.begin () + 1; it < trees.end () - 1;
it += 2 /* Increment + skip comma */)
{
- expand_derive (item, *it);
+ expand_derive (item, std::move (*it));
}
}
}
class ExpandVisitor : public AST::ASTVisitor
{
public:
- ExpandVisitor (MacroExpander &expander, ProcMacroExpander &proc_expander)
- : expander (expander), proc_expander (proc_expander)
- {}
+ ExpandVisitor (MacroExpander &expander) : expander (expander) {}
/* Expand all of the macro invocations currently contained in a crate */
void go (AST::Crate &crate);
value->accept_vis (*this);
auto final_fragment = expander.take_expanded_fragment ();
+ auto proc_macro_fragment
+ = expander.take_expanded_proc_macro_fragment ();
// FIXME: Is that correct? It seems *extremely* dodgy
if (final_fragment.should_expand ())
}
}
}
+ else if (proc_macro_fragment.should_expand ())
+ {
+ if (proc_macro_fragment.should_overwrite ())
+ it = values.erase (it);
+ else
+ it++;
+ for (auto &node : proc_macro_fragment.get_nodes ())
+ {
+ auto new_node = extractor (node);
+ if (new_node != nullptr)
+ {
+ it = values.insert (it, std::move (new_node));
+ it++;
+ }
+ }
+ }
else
{
++it;
template <typename T> void visit_inner_attrs (T &item);
template <typename T>
- void expand_derive (const T &item, std::unique_ptr<AST::TokenTree> &trait);
+ void expand_derive (T &item, std::unique_ptr<AST::TokenTree> trait);
- template <typename T>
- void expand_derive (const T &item, AST::DelimTokenTree &attr);
+ template <typename T> void expand_derive (T &item, AST::DelimTokenTree &attr);
template <typename T> void visit_attrs_with_derive (T &item);
private:
MacroExpander &expander;
- ProcMacroExpander &proc_expander;
};
} // namespace Rust
#include "rust-parse.h"
#include "rust-cfg-strip.h"
#include "rust-early-name-resolver.h"
+#include "rust-session-manager.h"
+#include "rust-proc-macro.h"
namespace Rust {
return fragment;
}
+
+// TODO: Move to early name resolver ?
+void
+MacroExpander::import_proc_macros (std::string extern_crate)
+{
+ auto path = session.extern_crates.find (extern_crate);
+ if (path == session.extern_crates.end ())
+ {
+ // Extern crate path is not available.
+ // FIXME: Emit error
+ rust_error_at (Location (), "Cannot find requested proc macro crate");
+ gcc_unreachable ();
+ }
+ auto macros = load_macros (path->second);
+
+ std::string prefix = extern_crate + "::";
+ for (auto ¯o : macros)
+ {
+ switch (macro.tag)
+ {
+ case ProcMacro::CUSTOM_DERIVE:
+ rust_debug ("Found one derive proc macro.");
+ mappings->insert_derive_proc_macro (
+ std::make_pair (extern_crate,
+ macro.payload.custom_derive.trait_name),
+ macro.payload.custom_derive);
+ break;
+ case ProcMacro::ATTR:
+ rust_debug ("Found one attribute proc macro.");
+ mappings->insert_attribute_proc_macro (
+ std::make_pair (extern_crate, macro.payload.attribute.name),
+ macro.payload.attribute);
+ break;
+ case ProcMacro::BANG:
+ rust_debug ("Found one bang proc macro.");
+ mappings->insert_bang_proc_macro (
+ std::make_pair (extern_crate, macro.payload.bang.name),
+ macro.payload.bang);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+}
+
+void
+MacroExpander::parse_procmacro_output (ProcMacro::TokenStream ts, bool derive)
+{
+ ProcMacroInvocLexer lex (convert (ts));
+ Parser<ProcMacroInvocLexer> parser (lex);
+
+ std::vector<AST::SingleASTNode> nodes;
+ switch (peek_context ())
+ {
+ case ContextType::ITEM:
+ while (lex.peek_token ()->get_id () != END_OF_FILE)
+ {
+ auto result = parser.parse_item (false);
+ if (result == nullptr)
+ break;
+ nodes.push_back ({std::move (result)});
+ }
+ break;
+ case ContextType::BLOCK:
+ while (lex.peek_token ()->get_id () != END_OF_FILE)
+ {
+ auto result = parser.parse_stmt ();
+ if (result == nullptr)
+ break;
+ nodes.push_back ({std::move (result)});
+ }
+ break;
+ case ContextType::TRAIT:
+ case ContextType::IMPL:
+ case ContextType::TRAIT_IMPL:
+ case ContextType::EXTERN:
+ case ContextType::TYPE:
+ default:
+ gcc_unreachable ();
+ }
+
+ if (parser.has_errors ())
+ set_expanded_proc_macro_fragment (AST::Fragment::create_error ());
+ else
+ set_expanded_proc_macro_fragment (
+ {nodes, std::vector<std::unique_ptr<AST::Token>> (), !derive});
+}
+
} // namespace Rust
#include "rust-early-name-resolver.h"
#include "rust-name-resolver.h"
#include "rust-macro-invoc-lexer.h"
+#include "rust-proc-macro-invoc-lexer.h"
+#include "rust-token-converter.h"
+#include "rust-ast-collector.h"
+#include "rust-system.h"
+#include "libproc_macro/proc_macro.h"
// Provides objects and method prototypes for macro expansion
: cfg (cfg), crate (crate), session (session),
sub_stack (SubstitutionScope ()),
expanded_fragment (AST::Fragment::create_error ()),
+ expanded_proc_macro_fragment (AST::Fragment::create_error ()),
has_changed_flag (false), resolver (Resolver::Resolver::get ()),
mappings (Analysis::Mappings::get ())
{}
return fragment;
}
+ void set_expanded_proc_macro_fragment (AST::Fragment &&fragment)
+ {
+ if (!fragment.is_error ())
+ has_changed_flag = true;
+
+ expanded_proc_macro_fragment = std::move (fragment);
+ }
+
+ AST::Fragment take_expanded_proc_macro_fragment ()
+ {
+ auto fragment = std::move (expanded_proc_macro_fragment);
+ expanded_proc_macro_fragment = AST::Fragment::create_error ();
+
+ return fragment;
+ }
+
+ void import_proc_macros (std::string extern_crate);
+
+ template <typename T>
+ void expand_derive_proc_macro (T &item, std::string &trait_name)
+ {
+ ProcMacro::CustomDerive macro;
+
+ // FIXME: Resolve crate name
+ std::string crate = "";
+ std::string name = trait_name;
+
+ if (!mappings->lookup_derive_proc_macro (std::make_pair (crate, name),
+ macro))
+ {
+ // FIXME: Resolve this path segment instead of taking it directly.
+ import_proc_macros (crate);
+ if (!mappings->lookup_derive_proc_macro (std::make_pair (crate, name),
+ macro))
+ {
+ rust_error_at (Location (), "procedural macro %s not found",
+ name.c_str ());
+ rust_assert (false);
+ }
+ }
+
+ std::vector<TokenPtr> tokens;
+ AST::TokenCollector collector (tokens);
+
+ collector.visit (item);
+
+ auto c = collector.collect_tokens ();
+ std::vector<const_TokenPtr> vec (c.cbegin (), c.cend ());
+
+ parse_procmacro_output (macro.macro (convert (vec)), true);
+ }
+
+ template <typename T>
+ void expand_bang_proc_macro (T &item, AST::SimplePath &path)
+ {
+ ProcMacro::Bang macro;
+
+ std::string crate = path.get_segments ()[0].get_segment_name ();
+ std::string name = path.get_segments ()[1].get_segment_name ();
+ if (!mappings->lookup_bang_proc_macro (std::make_pair (crate, name), macro))
+ {
+ // FIXME: Resolve this path segment instead of taking it directly.
+ import_proc_macros (crate);
+
+ if (!mappings->lookup_bang_proc_macro (std::make_pair (crate, name),
+ macro))
+ {
+ rust_error_at (Location (), "procedural macro %s not found",
+ name.c_str ());
+ rust_assert (false);
+ }
+ }
+
+ std::vector<TokenPtr> tokens;
+ AST::TokenCollector collector (tokens);
+
+ collector.visit (item);
+
+ auto c = collector.collect_tokens ();
+ std::vector<const_TokenPtr> vec (c.cbegin (), c.cend ());
+
+ parse_procmacro_output (macro.macro (convert (vec)), false);
+ }
+
+ template <typename T>
+ void expand_attribute_proc_macro (T &item, AST::SimplePath &path)
+ {
+ ProcMacro::Attribute macro;
+
+ std::string crate = path.get_segments ()[0].get_segment_name ();
+ std::string name = path.get_segments ()[1].get_segment_name ();
+ if (!mappings->lookup_attribute_proc_macro (std::make_pair (crate, name),
+ macro))
+ {
+ // FIXME: Resolve this path segment instead of taking it directly.
+ import_proc_macros (crate);
+ if (!mappings->lookup_attribute_proc_macro (std::make_pair (crate,
+ name),
+ macro))
+ {
+ rust_error_at (Location (), "procedural macro %s not found",
+ name.c_str ());
+ rust_assert (false);
+ }
+ }
+
+ std::vector<TokenPtr> tokens;
+ AST::TokenCollector collector (tokens);
+
+ collector.visit (item);
+
+ auto c = collector.collect_tokens ();
+ std::vector<const_TokenPtr> vec (c.cbegin (), c.cend ());
+
+ // FIXME: Handle attributes
+ parse_procmacro_output (
+ macro.macro (ProcMacro::TokenStream::make_tokenstream (), convert (vec)),
+ false);
+ }
+
/**
* Has the MacroExpander expanded a macro since its state was last reset?
*/
AST::MacroInvocation *get_last_invocation () { return last_invoc; }
private:
+ void parse_procmacro_output (ProcMacro::TokenStream ts, bool derive);
+
AST::Crate &crate;
Session &session;
SubstitutionScope sub_stack;
std::vector<ContextType> context;
AST::Fragment expanded_fragment;
+ AST::Fragment expanded_proc_macro_fragment;
bool has_changed_flag;
AST::MacroRulesDefinition *last_def;
--- /dev/null
+// Copyright (C) 2020-2023 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-proc-macro-invoc-lexer.h"
+#include "rust-token.h"
+
+namespace Rust {
+
+const_TokenPtr
+ProcMacroInvocLexer::peek_token (int n)
+{
+ if ((offs + n) >= token_stream.size ())
+ return Token::make (END_OF_FILE, Location ());
+
+ return token_stream.at (offs + n);
+}
+
+// Advances current token to n + 1 tokens ahead of current position.
+void
+ProcMacroInvocLexer::skip_token (int n)
+{
+ offs += (n + 1);
+}
+
+void
+ProcMacroInvocLexer::split_current_token (TokenId new_left, TokenId new_right)
+{
+ auto ¤t_token = token_stream.at (offs);
+ auto current_pos = token_stream.begin () + offs;
+
+ auto l_tok = Token::make (new_left, current_token->get_locus ());
+ auto r_tok = Token::make (new_right, current_token->get_locus ());
+
+ token_stream.erase (current_pos);
+
+ // `insert` inserts before the specified position, so we insert the right one
+ // then the left
+ token_stream.insert (current_pos, l_tok);
+ token_stream.insert (current_pos, r_tok);
+}
+
+} // namespace Rust
--- /dev/null
+// Copyright (C) 2020-2023 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_PROC_MACRO_INVOC_LEXER_H
+#define RUST_PROC_MACRO_INVOC_LEXER_H
+
+#include "rust-lex.h"
+
+namespace Rust {
+class ProcMacroInvocLexer
+{
+public:
+ ProcMacroInvocLexer (std::vector<const_TokenPtr> stream)
+ : offs (0), token_stream (std::move (stream))
+ {}
+
+ // Returns token n tokens ahead of current position.
+ const_TokenPtr peek_token (int n);
+
+ // Peeks the current token.
+ const_TokenPtr peek_token () { return peek_token (0); }
+
+ // Advances current token to n + 1 tokens ahead of current position.
+ void skip_token (int n);
+
+ // Skips the current token.
+ void skip_token () { skip_token (0); }
+
+ // Splits the current token into two. Intended for use with nested generics
+ // closes (i.e. T<U<X>> where >> is wrongly lexed as one token). Note that
+ // this will only work with "simple" tokens like punctuation.
+ void split_current_token (TokenId new_left, TokenId new_right);
+
+ std::string get_filename () const
+ {
+ // FIXME
+ gcc_unreachable ();
+ return "FIXME";
+ }
+
+ size_t get_offs () const { return offs; }
+
+private:
+ size_t offs;
+ std::vector<const_TokenPtr> token_stream;
+};
+} // namespace Rust
+
+#endif /* ! RUST_PROC_MACRO_INVOC_LEXER_H */
array->macros + array->length);
}
-void
-ProcMacroExpander::import_proc_macros (std::string extern_crate)
-{
- auto path = session.extern_crates.find (extern_crate);
- if (path == session.extern_crates.end ())
- {
- // Extern crate path is not available.
- // FIXME: Emit error
- rust_error_at (Location (), "Cannot find requested proc macro crate");
- gcc_unreachable ();
- }
- auto macros = load_macros (path->second);
-
- std::string prefix = extern_crate + "::";
- for (auto ¯o : macros)
- {
- switch (macro.tag)
- {
- case ProcMacro::CUSTOM_DERIVE:
- rust_debug ("Found one derive proc macro.");
- mappings->insert_derive_proc_macro (
- std::make_pair (extern_crate,
- macro.payload.custom_derive.trait_name),
- macro.payload.custom_derive);
- break;
- case ProcMacro::ATTR:
- rust_debug ("Found one attribute proc macro.");
- mappings->insert_attribute_proc_macro (
- std::make_pair (extern_crate, macro.payload.attribute.name),
- macro.payload.attribute);
- break;
- case ProcMacro::BANG:
- rust_debug ("Found one bang proc macro.");
- mappings->insert_bang_proc_macro (
- std::make_pair (extern_crate, macro.payload.bang.name),
- macro.payload.bang);
- break;
- default:
- gcc_unreachable ();
- }
- }
-}
-
} // namespace Rust
#ifndef RUST_PROC_MACRO_H
#define RUST_PROC_MACRO_H
-#include <string>
-#include "rust-hir-map.h"
-#include "rust-name-resolver.h"
-#include "rust-session-manager.h"
-#include "rust-ast.h"
-#include "rust-ast-collector.h"
-#include "rust-token-converter.h"
#include "libproc_macro/proc_macro.h"
namespace Rust {
-
/**
- * Load a procedural macro library and return a pointer to it's entrypoint.
+ * Load a procedural macro library and collect its entrypoints.
*
* @param The path to the shared object file to load.
*/
const std::vector<ProcMacro::Procmacro>
load_macros (std::string path);
-class ProcMacroExpander
-{
-public:
- ProcMacroExpander (Session &session)
- : session (session), has_changed_flag (false),
- resolver (Resolver::Resolver::get ()),
- mappings (Analysis::Mappings::get ())
-
- {}
-
- ~ProcMacroExpander () = default;
-
- void import_proc_macros (std::string extern_crate);
-
- template <typename T>
- void expand_derive_proc_macro (T &item, std::string &trait_name)
- {}
-
- template <typename T>
- void expand_bang_proc_macro (T &item, AST::SimplePath &path)
- {}
-
- template <typename T>
- void expand_attribute_proc_macro (T &item, AST::SimplePath &path)
- {
- ProcMacro::Attribute macro;
-
- std::string crate = path.get_segments ()[0].get_segment_name ();
- std::string name = path.get_segments ()[1].get_segment_name ();
- if (!mappings->lookup_attribute_proc_macro (std::make_pair (crate, name),
- macro))
- {
- // FIXME: Resolve this path segment instead of taking it directly.
- import_proc_macros (crate);
- }
-
- if (!mappings->lookup_attribute_proc_macro (std::make_pair (crate, name),
- macro))
- {
- rust_error_at (Location (), "procedural macro %s not found",
- name.c_str ());
- rust_assert (false);
- }
- // FIXME: Attach result back to the ast
- std::vector<TokenPtr> tokens;
- AST::TokenCollector collector (tokens);
-
- collector.visit (item);
-
- std::vector<const_TokenPtr> vec;
- for (auto i : collector.collect_tokens ())
- {
- vec.push_back (std::const_pointer_cast<Token> (i));
- }
-
- // FIXME: Handle attributes
- macro.macro (ProcMacro::TokenStream::make_tokenstream (), convert (vec));
- }
-
- bool has_changed () const { return has_changed_flag; }
-
- void reset_changed_state () { has_changed_flag = false; }
-
-private:
- Session &session;
- bool has_changed_flag;
-
-public:
- Resolver::Resolver *resolver;
- Analysis::Mappings *mappings;
-};
-
} // namespace Rust
#endif /* ! RUST_PROC_MACRO_H */
/* expand by calling cxtctxt object's monotonic_expander's expand_crate
* method. */
MacroExpander expander (crate, cfg, *this);
- ProcMacroExpander proc_expander (*this);
while (!fixed_point_reached && iterations < cfg.recursion_limit)
{
CfgStrip ().go (crate);
Resolver::EarlyNameResolver ().go (crate);
- ExpandVisitor (expander, proc_expander).go (crate);
+ ExpandVisitor (expander).go (crate);
fixed_point_reached = !expander.has_changed ();
expander.reset_changed_state ();