aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorArthur Cohen <arthur.cohen@embecosm.com>2023-07-27 10:06:13 +0200
committerArthur Cohen <arthur.cohen@embecosm.com>2024-01-16 19:00:27 +0100
commitf7084d487286e4c7ba2336b7fa567fc59def4c01 (patch)
treeb120e28313820c8b9742e10e0c1e073cdfe44eb3 /gcc
parent2bde73fa4669dfb34a981aa91b750e5ca8433491 (diff)
downloadgcc-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.in1
-rw-r--r--gcc/rust/resolve/rust-early-name-resolver-2.0.cc164
-rw-r--r--gcc/rust/resolve/rust-early-name-resolver-2.0.h84
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