]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: nr2.0: Add Early name resolution visitor
authorArthur Cohen <arthur.cohen@embecosm.com>
Thu, 27 Jul 2023 08:06:13 +0000 (10:06 +0200)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 16 Jan 2024 18:00:27 +0000 (19:00 +0100)
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.

gcc/rust/Make-lang.in
gcc/rust/resolve/rust-early-name-resolver-2.0.cc [new file with mode: 0644]
gcc/rust/resolve/rust-early-name-resolver-2.0.h [new file with mode: 0644]

index da4135ab6d0a34d177b8bd5bc02d9f8f9ce335c5..1df82f1598b16d0eeb535bba2cf944f5486c4982 100644 (file)
@@ -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 (file)
index 0000000..df72d6e
--- /dev/null
@@ -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 (file)
index 0000000..fe83cf4
--- /dev/null
@@ -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