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