diff options
author | Arthur Cohen <arthur.cohen@embecosm.com> | 2023-07-27 10:06:13 +0200 |
---|---|---|
committer | Arthur Cohen <arthur.cohen@embecosm.com> | 2024-01-16 19:00:27 +0100 |
commit | f7084d487286e4c7ba2336b7fa567fc59def4c01 (patch) | |
tree | b120e28313820c8b9742e10e0c1e073cdfe44eb3 /gcc | |
parent | 2bde73fa4669dfb34a981aa91b750e5ca8433491 (diff) | |
download | gcc-f7084d487286e4c7ba2336b7fa567fc59def4c01.zip gcc-f7084d487286e4c7ba2336b7fa567fc59def4c01.tar.gz gcc-f7084d487286e4c7ba2336b7fa567fc59def4c01.tar.bz2 |
gccrs: nr2.0: Add Early name resolution visitor
This visitor takes care of resolving macro invocations, procedural macros
and imports - it is used in conjunction with the `TopLevel` pass and
the macro expander.
gcc/rust/ChangeLog:
* Make-lang.in: Add new object file.
* resolve/rust-early-name-resolver-2.0.cc: New file.
* resolve/rust-early-name-resolver-2.0.h: New file.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/rust/Make-lang.in | 1 | ||||
-rw-r--r-- | gcc/rust/resolve/rust-early-name-resolver-2.0.cc | 164 | ||||
-rw-r--r-- | gcc/rust/resolve/rust-early-name-resolver-2.0.h | 84 |
3 files changed, 249 insertions, 0 deletions
diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index da4135a..1df82f1 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -113,6 +113,7 @@ GRS_OBJS = \ rust/rust-name-resolution-context.o \ rust/rust-default-resolver.o \ rust/rust-toplevel-name-resolver-2.0.o \ + rust/rust-early-name-resolver-2.0.o \ rust/rust-early-name-resolver.o \ rust/rust-name-resolver.o \ rust/rust-ast-resolve.o \ diff --git a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc new file mode 100644 index 0000000..df72d6e --- /dev/null +++ b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc @@ -0,0 +1,164 @@ +// 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-early-name-resolver-2.0.h" +#include "rust-ast-full.h" +#include "rust-toplevel-name-resolver-2.0.h" + +namespace Rust { +namespace Resolver2_0 { + +Early::Early (NameResolutionContext &ctx) : DefaultResolver (ctx) {} + +void +Early::go (AST::Crate &crate) +{ + // First we go through TopLevel resolution to get all our declared items + auto toplevel = TopLevel (ctx); + toplevel.go (crate); + + textual_scope.push (); + + // Then we proceed to the proper "early" name resolution: Import and macro + // name resolution + for (auto &item : crate.items) + item->accept_vis (*this); + + textual_scope.pop (); +} + +void +Early::TextualScope::push () +{ + // push a new empty scope + scopes.emplace_back (); +} + +void +Early::TextualScope::pop () +{ + rust_assert (!scopes.empty ()); + + scopes.pop_back (); +} + +void +Early::TextualScope::insert (std::string name, NodeId id) +{ + rust_assert (!scopes.empty ()); + + // we can ignore the return value as we always want the latest defined macro + // to shadow a previous one - so if two macros have the same name and get + // inserted with the same key, it's not a bug + scopes.back ().insert ({name, id}); +} + +tl::optional<NodeId> +Early::TextualScope::get (const std::string &name) +{ + for (auto iterator = scopes.rbegin (); iterator != scopes.rend (); iterator++) + { + auto scope = *iterator; + auto found = scope.find (name); + if (found != scope.end ()) + return found->second; + } + + return tl::nullopt; +} + +void +Early::visit (AST::MacroRulesDefinition &def) +{ + DefaultResolver::visit (def); + + textual_scope.insert (def.get_rule_name ().as_string (), def.get_node_id ()); +} + +void +Early::visit (AST::BlockExpr &block) +{ + textual_scope.push (); + + DefaultResolver::visit (block); + + textual_scope.pop (); +} + +void +Early::visit (AST::Module &module) +{ + textual_scope.push (); + + DefaultResolver::visit (module); + + textual_scope.pop (); +} + +void +Early::visit (AST::MacroInvocation &invoc) +{ + auto path = invoc.get_invoc_data ().get_path (); + + // When a macro is invoked by an unqualified identifier (not part of a + // multi-part path), it is first looked up in textual scoping. If this does + // not yield any results, then it is looked up in path-based scoping. If the + // macro's name is qualified with a path, then it is only looked up in + // path-based scoping. + + // https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope + + tl::optional<NodeId> definition = tl::nullopt; + if (path.get_segments ().size () == 1) + definition = textual_scope.get (path.get_final_segment ().as_string ()); + + // we won't have changed `definition` from `nullopt` if there are more + // than one segments in our path + if (!definition.has_value ()) + definition = ctx.macros.resolve_path (path); + + // if the definition still does not have a value, then it's an error + if (!definition.has_value ()) + { + rust_error_at (invoc.get_locus (), ErrorCode::E0433, + "could not resolve macro invocation"); + return; + } + + // now do we need to keep mappings or something? or insert "uses" into our + // ForeverStack? can we do that? are mappings simpler? +} + +void +Early::visit (AST::UseDeclaration &use) +{} + +void +Early::visit (AST::UseTreeRebind &use) +{} + +void +Early::visit (AST::UseTreeList &use) +{} + +void +Early::visit (AST::UseTreeGlob &use) +{} + +} // namespace Resolver2_0 +} // namespace Rust diff --git a/gcc/rust/resolve/rust-early-name-resolver-2.0.h b/gcc/rust/resolve/rust-early-name-resolver-2.0.h new file mode 100644 index 0000000..fe83cf4 --- /dev/null +++ b/gcc/rust/resolve/rust-early-name-resolver-2.0.h @@ -0,0 +1,84 @@ +// 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_EARLY_NAME_RESOLVER_2_0_H +#define RUST_EARLY_NAME_RESOLVER_2_0_H + +#include "optional.h" +#include "rust-ast.h" +#include "rust-ast-visitor.h" +#include "rust-name-resolution-context.h" +#include "rust-default-resolver.h" + +namespace Rust { +namespace Resolver2_0 { + +class Early : public DefaultResolver +{ + using DefaultResolver::visit; + +public: + Early (NameResolutionContext &ctx); + + void go (AST::Crate &crate); + + // we need to handle definitions for textual scoping + void visit (AST::MacroRulesDefinition &) override; + + // as well as lexical scopes + void visit (AST::BlockExpr &) override; + void visit (AST::Module &) override; + + void visit (AST::MacroInvocation &) override; + void visit (AST::UseDeclaration &) override; + void visit (AST::UseTreeRebind &) override; + void visit (AST::UseTreeList &) override; + void visit (AST::UseTreeGlob &) override; + +private: + /** + * Macros can either be resolved through textual scoping or regular path + * scoping - which this class represents. Textual scoping works similarly to a + * "simple" name resolution algorith, with the addition of "shadowing". Each + * time a new lexical scope is entered, we push a new map onto the stack, in + * which newly defined macros are added. The latest defined macro is the one + * that takes precedence. When resolving a macro invocation to its definition, + * we walk up the stack and look for a definition in each of the map until we + * find one. Otherwise, the macro invocation is unresolved, and goes through + * regular path resolution. + */ + class TextualScope + { + public: + void push (); + void pop (); + + void insert (std::string name, NodeId id); + tl::optional<NodeId> get (const std::string &name); + + private: + std::vector<std::unordered_map<std::string, NodeId>> scopes; + }; + + TextualScope textual_scope; +}; + +} // namespace Resolver2_0 +} // namespace Rust + +#endif // ! RUST_EARLY_NAME_RESOLVER_2_0_H |