https://gcc.gnu.org/g:435fdda0f5bb1b71b42a6b971463803a410596cc
commit r17-1866-g435fdda0f5bb1b71b42a6b971463803a410596cc Author: Arthur Cohen <[email protected]> Date: Tue Mar 24 13:33:49 2026 +0100 gccrs: nr: Add imports to types NS, add NR flattening 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. Diff: --- gcc/rust/resolve/rust-early-name-resolver-2.0.cc | 73 +++++++++++++++++++++--- gcc/rust/resolve/rust-name-resolution-context.cc | 70 +++++++++++++++++++++++ gcc/rust/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 058207f1f4a4..4616323e6a8c 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 bbd8f07dd7bb..efe746fe9d34 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 c5b2d0b1c00c..6f18115baecd 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 9872cc0e7644..8a6d28882c90 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);
