From: Arthur Cohen <[email protected]>

gcc/rust/ChangeLog:

        * resolve/rust-early-name-resolver-2.0.cc: Properly resolve and insert 
imports in the
        types NS.
        * resolve/rust-name-resolution-context.cc (find_leaf_definition_inner): 
New.
        (NameResolutionContext::find_leaf_definition): New.
        (NameResolutionContext::flatten): New.
        * resolve/rust-name-resolution-context.h: Declare flattening functions.
        * rust-session-manager.cc (Session::compile_crate): Call NR flattening.
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/6c30a095d55ac96fb2ef397d5eb9cb4aa0bd062c

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4551

 .../resolve/rust-early-name-resolver-2.0.cc   | 73 ++++++++++++++++---
 .../resolve/rust-name-resolution-context.cc   | 70 ++++++++++++++++++
 .../resolve/rust-name-resolution-context.h    | 33 +++++++++
 gcc/rust/rust-session-manager.cc              |  4 +-
 4 files changed, 170 insertions(+), 10 deletions(-)

diff --git a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc 
b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc
index 058207f1f..4616323e6 100644
--- a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc
+++ b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc
@@ -122,6 +122,40 @@ bool
 Early::resolve_rebind_import (NodeId use_dec_id,
                              TopLevel::ImportKind &&rebind_import)
 {
+  NodeId import_id = UNKNOWN_NODEID;
+  auto &path = rebind_import.to_resolve;
+  auto &rebind = rebind_import.rebind.value ();
+
+  switch (rebind.get_new_bind_type ())
+    {
+    case AST::UseTreeRebind::NewBindType::IDENTIFIER:
+      import_id = rebind.get_node_id ();
+      break;
+    case AST::UseTreeRebind::NewBindType::NONE:
+      {
+       const auto &segments = path.get_segments ();
+       // We don't want to insert `self` with `use module::self`
+       if (path.get_final_segment ().is_lower_self_seg ())
+         {
+           // Erroneous `self` or `{self}` use declaration
+           if (segments.size () == 1)
+             break;
+           import_id = segments[segments.size () - 2].get_node_id ();
+         }
+       else
+         {
+           import_id = path.get_final_segment ().get_node_id ();
+         }
+       break;
+      }
+    case AST::UseTreeRebind::NewBindType::WILDCARD:
+      // nothing
+      break;
+    }
+
+  if (ctx.lookup (import_id))
+    return true;
+
   auto definitions = resolve_path_in_all_ns (rebind_import.to_resolve);
 
   // if we've found at least one definition, then we're good
@@ -406,15 +440,22 @@ Early::finalize_simple_import (const Early::ImportPair 
&mapping)
 {
   // FIXME: We probably need to store namespace information
 
-  auto locus = mapping.import_kind.to_resolve.get_locus ();
+  auto import = mapping.import_kind.to_resolve;
+  auto import_id = import.get_final_segment ().get_node_id ();
   auto data = mapping.data;
-  auto identifier
-    = mapping.import_kind.to_resolve.get_final_segment ().get_segment_name ();
+  auto identifier = import.get_final_segment ().get_segment_name ();
 
   for (auto &&definition : data.definitions ())
-    toplevel
-      .insert_or_error_out (
-       identifier, locus, definition.first.get_node_id (), definition.second 
/* TODO: This isn't clear - it would be better if it was called .ns or 
something */);
+    {
+      dirty = true;
+
+      ctx.map_usage (Usage (import_id),
+                    Definition (definition.first.get_node_id ()));
+
+      toplevel
+       .insert_or_error_out (identifier,
+                             import.get_locus (), import_id, definition.second 
/* TODO: This isn't clear - it would be better if it was called .ns or 
something */);
+    }
 }
 
 void
@@ -446,6 +487,7 @@ Early::finalize_rebind_import (const Early::ImportPair 
&mapping)
   auto &rebind = mapping.import_kind.rebind.value ();
   auto data = mapping.data;
 
+  NodeId import_id = UNKNOWN_NODEID;
   location_t locus = UNKNOWN_LOCATION;
   std::string declared_name;
 
@@ -455,6 +497,7 @@ Early::finalize_rebind_import (const Early::ImportPair 
&mapping)
     case AST::UseTreeRebind::NewBindType::IDENTIFIER:
       declared_name = rebind.get_identifier ().as_string ();
       locus = rebind.get_identifier ().get_locus ();
+      import_id = rebind.get_node_id ();
       break;
     case AST::UseTreeRebind::NewBindType::NONE:
       {
@@ -466,9 +509,13 @@ Early::finalize_rebind_import (const Early::ImportPair 
&mapping)
            if (segments.size () == 1)
              break;
            declared_name = segments[segments.size () - 2].as_string ();
+           import_id = segments[segments.size () - 2].get_node_id ();
          }
        else
-         declared_name = path.get_final_segment ().as_string ();
+         {
+           declared_name = path.get_final_segment ().as_string ();
+           import_id = path.get_final_segment ().get_node_id ();
+         }
        locus = path.get_final_segment ().get_locus ();
        break;
       }
@@ -478,8 +525,16 @@ Early::finalize_rebind_import (const Early::ImportPair 
&mapping)
     }
 
   for (auto &&definition : data.definitions ())
-    toplevel.insert_or_error_out (
-      declared_name, locus, definition.first.get_node_id (), definition.second 
/* TODO: This isn't clear - it would be better if it was called .ns or 
something */);
+    {
+      dirty = true;
+
+      ctx.map_usage (Usage (import_id),
+                    Definition (definition.first.get_node_id ()));
+
+      toplevel
+       .insert_or_error_out (declared_name,
+                             path.get_locus (), import_id, definition.second 
/* TODO: This isn't clear - it would be better if it was called .ns or 
something */);
+    }
 }
 
 void
diff --git a/gcc/rust/resolve/rust-name-resolution-context.cc 
b/gcc/rust/resolve/rust-name-resolution-context.cc
index bbd8f07dd..efe746fe9 100644
--- a/gcc/rust/resolve/rust-name-resolution-context.cc
+++ b/gcc/rust/resolve/rust-name-resolution-context.cc
@@ -330,5 +330,75 @@ NameResolutionContext::scoped (Rib::Kind rib_kind, 
Namespace ns,
     }
 }
 
+static tl::expected<Definition, NameResolutionContext::LookupFinalizeError>
+find_leaf_definition_inner (const Usage &key,
+                           const std::map<Usage, Definition> &resolved_nodes,
+                           std::set<Usage> &keys_seen)
+{
+  auto original_definition = resolved_nodes.find (key);
+  auto possible_import = Usage (original_definition->second.id);
+
+  if (original_definition == resolved_nodes.end ())
+    return tl::make_unexpected (
+      NameResolutionContext::LookupFinalizeError::NoDefinition);
+
+  if (!keys_seen.insert (key).second)
+    return tl::make_unexpected (
+      NameResolutionContext::LookupFinalizeError::Loop);
+
+  if (resolved_nodes.find (possible_import) == resolved_nodes.end ())
+    return original_definition->second;
+
+  // We're dealing with an import - a reference to another
+  // definition. Go through the chain and update the original key's
+  // corresponding definition.
+  return find_leaf_definition_inner (possible_import, resolved_nodes,
+                                    keys_seen);
+}
+
+tl::expected<Definition, NameResolutionContext::LookupFinalizeError>
+NameResolutionContext::find_leaf_definition (const NodeId &key) const
+{
+  std::set<Usage> keys_seen;
+
+  return find_leaf_definition_inner (Usage (key), resolved_nodes, keys_seen);
+}
+
+void
+NameResolutionContext::flatten ()
+{
+  rust_debug ("[ARTHUR] FINALIZING EARLY NR!!!!");
+
+  for (auto &kv : resolved_nodes)
+    rust_debug ("[ARTHUR] [resolved_nodes]: %d -> %d", kv.first.id,
+               kv.second.id);
+
+  for (auto &k_v : resolved_nodes)
+    {
+      // Loop detection
+      auto keys_seen = std::set<Usage> ();
+
+      auto result
+       = find_leaf_definition_inner (k_v.first, resolved_nodes, keys_seen);
+
+      if (!result)
+       {
+         // Trigger an ICE if we haven't found a definition because that's
+         // really weird
+         rust_assert (result.error () != LookupFinalizeError::NoDefinition);
+
+         rust_error_at (UNDEF_LOCATION, "import loop");
+         continue;
+       }
+
+      // Replace the Definition for this Usage in the map. This may be a no-op.
+      k_v.second = result.value ();
+    }
+
+  for (auto &kv : resolved_nodes)
+    rust_debug ("[ARTHUR] [resolved_nodes]: %d -> %d", kv.first.id,
+               kv.second.id);
+}
+
 } // namespace Resolver2_0
 } // namespace Rust
diff --git a/gcc/rust/resolve/rust-name-resolution-context.h 
b/gcc/rust/resolve/rust-name-resolution-context.h
index c5b2d0b1c..6f18115ba 100644
--- a/gcc/rust/resolve/rust-name-resolution-context.h
+++ b/gcc/rust/resolve/rust-name-resolution-context.h
@@ -805,6 +805,39 @@ public:
   /* If declared with #[prelude_import], the current standard library module */
   tl::optional<NodeId> prelude;
 
+  enum class LookupFinalizeError
+  {
+    // Impossible - we did not find any definition corresponding to a Usage.
+    // This is an internal compiler error
+    NoDefinition,
+    // There was a loop in the map, such as an import resolving to another
+    // import which eventually resolved to the original import. Report the 
error
+    // and stop the pipeline
+    Loop,
+  };
+
+  tl::expected<Definition, LookupFinalizeError>
+  find_leaf_definition (const NodeId &key) const;
+
+  /**
+   * We've now collected every definition and import, and errored out when
+   * necessary if multiple definitions are colliding. Do a final flattening of
+   * the name resolution context to make it easier to digest for the late name
+   * resolution and type-checker. This basically turns the `resolved_nodes` map
+   * from a linked-list-like map to a regular, flat hashmap.
+   *
+   * FIXME: The documentation is wrong, this needs to also run after all usages
+   * have been *resolved* so after Late as well!!!
+   *
+   * TODO: Should this return something like the ImmutableNameResolutionCtx? Or
+   * set it up at least? And instead of mutating the `resolved_nodes` map,
+   * create a new one for the ImmutableNameResolutionCtx?
+   * Actually, since Late uses the NRCtx directly we should mutate this. Most
+   * later passes don't look at this map. So let's go for side-effects in a 
void
+   * function, yipee.
+   */
+  void flatten ();
+
 private:
   /* Map of "usage" nodes which have been resolved to a "definition" node */
   std::map<Usage, Definition> resolved_nodes;
diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc
index 9872cc0e7..8a6d28882 100644
--- a/gcc/rust/rust-session-manager.cc
+++ b/gcc/rust/rust-session-manager.cc
@@ -650,7 +650,6 @@ Session::compile_crate (const char *filename)
     {
       options.target_data.dump_target_options ();
     }
-
   if (saw_errors ())
     return;
 
@@ -765,6 +764,9 @@ Session::compile_crate (const char *filename)
   if (last_step == CompileOptions::CompileStep::NameResolution)
     return;
 
+  // FIXME: Or run it within Late at the end?
+  name_resolution_ctx.flatten ();
+
   // resolution pipeline stage
   Resolver2_0::Late (name_resolution_ctx).go (parsed_crate);
 

base-commit: fd310b296e11d3ff85a1ecc3cb3687edefb7d589
-- 
2.54.0

Reply via email to