From: Arthur Cohen Date: Thu, 27 Jul 2023 08:06:13 +0000 (+0200) Subject: gccrs: nr2.0: Add Early name resolution visitor X-Git-Tag: basepoints/gcc-15~2251 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7084d487286e4c7ba2336b7fa567fc59def4c01;p=thirdparty%2Fgcc.git 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. --- diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index da4135ab6d0a..1df82f1598b1 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 000000000000..df72d6e27c3c --- /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 +// . + +#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 +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 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 000000000000..fe83cf4493b3 --- /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 +// . + +#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 get (const std::string &name); + + private: + std::vector> scopes; + }; + + TextualScope textual_scope; +}; + +} // namespace Resolver2_0 +} // namespace Rust + +#endif // ! RUST_EARLY_NAME_RESOLVER_2_0_H