| // Copyright (C) 2020-2024 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-ast-resolve-path.h" |
| #include "rust-ast-resolve-type.h" |
| #include "rust-hir-map.h" |
| #include "rust-path.h" |
| |
| namespace Rust { |
| namespace Resolver { |
| |
| ResolvePath::ResolvePath () : ResolverBase () {} |
| |
| NodeId |
| ResolvePath::go (AST::PathInExpression &expr) |
| { |
| ResolvePath resolver; |
| return resolver.resolve_path (expr); |
| } |
| |
| NodeId |
| ResolvePath::go (AST::QualifiedPathInExpression &expr) |
| { |
| ResolvePath resolver; |
| return resolver.resolve_path (expr); |
| } |
| |
| NodeId |
| ResolvePath::go (AST::SimplePath &expr) |
| { |
| ResolvePath resolver; |
| return resolver.resolve_path (expr); |
| } |
| |
| NodeId |
| ResolvePath::resolve_path (AST::PathInExpression &expr) |
| { |
| if (expr.is_lang_item ()) |
| return Analysis::Mappings::get ().get_lang_item_node ( |
| expr.get_lang_item ()); |
| |
| NodeId resolved_node_id = UNKNOWN_NODEID; |
| NodeId module_scope_id = resolver->peek_current_module_scope (); |
| NodeId previous_resolved_node_id = module_scope_id; |
| for (size_t i = 0; i < expr.get_segments ().size (); i++) |
| { |
| auto &segment = expr.get_segments ().at (i); |
| const AST::PathIdentSegment &ident_seg = segment.get_ident_segment (); |
| bool is_first_segment = i == 0; |
| resolved_node_id = UNKNOWN_NODEID; |
| |
| bool in_middle_of_path = i > 0; |
| if (in_middle_of_path && segment.is_lower_self_seg ()) |
| { |
| rust_error_at (segment.get_locus (), ErrorCode::E0433, |
| "%qs in paths can only be used in start position", |
| segment.as_string ().c_str ()); |
| return UNKNOWN_NODEID; |
| } |
| |
| NodeId crate_scope_id = resolver->peek_crate_module_scope (); |
| if (segment.is_crate_path_seg ()) |
| { |
| // what is the current crate scope node id? |
| module_scope_id = crate_scope_id; |
| previous_resolved_node_id = module_scope_id; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_name (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == module_scope_id); |
| else |
| resolver->insert_resolved_name (segment.get_node_id (), |
| module_scope_id); |
| continue; |
| } |
| else if (segment.is_super_path_seg ()) |
| { |
| if (module_scope_id == crate_scope_id) |
| { |
| rust_error_at (segment.get_locus (), |
| "cannot use %<super%> at the crate scope"); |
| return UNKNOWN_NODEID; |
| } |
| |
| module_scope_id = resolver->peek_parent_module_scope (); |
| previous_resolved_node_id = module_scope_id; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_name (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == module_scope_id); |
| else |
| resolver->insert_resolved_name (segment.get_node_id (), |
| module_scope_id); |
| continue; |
| } |
| |
| // resolve any generic args |
| if (segment.has_generic_args ()) |
| ResolveGenericArgs::go (segment.get_generic_args ()); |
| |
| // logic is awkward here there are a few cases |
| // |
| // T::Default |
| // mod::foo::impl_item |
| // super::super::module::item |
| // self |
| // self::foo |
| // self::foo::baz |
| // |
| // T::Default we can only resolve the T and cant do anything about Default |
| // its dependant on associated types |
| // |
| // mod::foo::impl_item |
| // we can resolve mod::foo but nothing about impl_item but we need to |
| // _always resolve generic arguments |
| // |
| // self is a simple single lookup |
| // |
| // we have module_scope_id for the next module_scope to lookup |
| // resolved_node_id is the thing we have resolve this segment to |
| // |
| // new algo? |
| // we can only use module resolution when the previous segment is either |
| // unknown or equal to this module_scope_id |
| // |
| // can only use old resolution when previous segment is unkown |
| |
| if (is_first_segment) |
| { |
| // name scope first |
| NodeId resolved_node = UNKNOWN_NODEID; |
| const CanonicalPath path |
| = CanonicalPath::new_seg (segment.get_node_id (), |
| ident_seg.as_string ()); |
| if (resolver->get_name_scope ().lookup (path, &resolved_node)) |
| { |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_name (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node); |
| else |
| resolver->insert_resolved_name (segment.get_node_id (), |
| resolved_node); |
| resolved_node_id = resolved_node; |
| } |
| // check the type scope |
| else if (resolver->get_type_scope ().lookup (path, &resolved_node)) |
| { |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_type (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node); |
| else |
| resolver->insert_resolved_type (segment.get_node_id (), |
| resolved_node); |
| resolved_node_id = resolved_node; |
| } |
| else if (segment.is_lower_self_seg ()) |
| { |
| module_scope_id = crate_scope_id; |
| previous_resolved_node_id = module_scope_id; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_name (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == module_scope_id); |
| else |
| resolver->insert_resolved_name (segment.get_node_id (), |
| module_scope_id); |
| continue; |
| } |
| else |
| { |
| // no error handling here since we might be able to resolve via |
| // the module hierarchy and handle errors at the end |
| } |
| } |
| |
| if (resolved_node_id == UNKNOWN_NODEID |
| && previous_resolved_node_id == module_scope_id) |
| { |
| tl::optional<CanonicalPath &> resolved_child |
| = mappings.lookup_module_child (module_scope_id, |
| ident_seg.as_string ()); |
| if (resolved_child.has_value ()) |
| { |
| NodeId resolved_node = resolved_child->get_node_id (); |
| if (resolver->get_name_scope ().decl_was_declared_here ( |
| resolved_node)) |
| { |
| resolved_node_id = resolved_node; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok |
| = resolver->lookup_resolved_name (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node); |
| else |
| resolver->insert_resolved_name (segment.get_node_id (), |
| resolved_node); |
| } |
| else if (resolver->get_type_scope ().decl_was_declared_here ( |
| resolved_node)) |
| { |
| resolved_node_id = resolved_node; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok |
| = resolver->lookup_resolved_type (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node); |
| else |
| resolver->insert_resolved_type (segment.get_node_id (), |
| resolved_node); |
| } |
| else |
| { |
| rust_error_at (segment.get_locus (), |
| "Cannot find path %<%s%> in this scope", |
| segment.as_string ().c_str ()); |
| return UNKNOWN_NODEID; |
| } |
| } |
| } |
| |
| bool did_resolve_segment = resolved_node_id != UNKNOWN_NODEID; |
| if (did_resolve_segment) |
| { |
| if (mappings.node_is_module (resolved_node_id) |
| || mappings.node_is_crate (resolved_node_id)) |
| { |
| module_scope_id = resolved_node_id; |
| } |
| previous_resolved_node_id = resolved_node_id; |
| } |
| else if (is_first_segment) |
| { |
| rust_error_at (segment.get_locus (), ErrorCode::E0433, |
| "Cannot find path %<%s%> in this scope", |
| segment.as_string ().c_str ()); |
| return UNKNOWN_NODEID; |
| } |
| } |
| |
| resolved_node = resolved_node_id; |
| if (resolved_node_id != UNKNOWN_NODEID) |
| { |
| // name scope first |
| if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id)) |
| { |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok |
| = resolver->lookup_resolved_name (expr.get_node_id (), &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node_id); |
| else |
| resolver->insert_resolved_name (expr.get_node_id (), |
| resolved_node_id); |
| } |
| // check the type scope |
| else if (resolver->get_type_scope ().decl_was_declared_here ( |
| resolved_node_id)) |
| { |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok |
| = resolver->lookup_resolved_type (expr.get_node_id (), &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node_id); |
| else |
| resolver->insert_resolved_type (expr.get_node_id (), |
| resolved_node_id); |
| } |
| else |
| { |
| rust_unreachable (); |
| } |
| } |
| return resolved_node_id; |
| } |
| |
| NodeId |
| ResolvePath::resolve_path (AST::QualifiedPathInExpression &expr) |
| { |
| auto &root_segment = expr.get_qualified_path_type (); |
| ResolveType::go (root_segment.get_type ()); |
| if (root_segment.has_as_clause ()) |
| ResolveType::go (root_segment.get_as_type_path ()); |
| |
| for (auto &segment : expr.get_segments ()) |
| { |
| // we cant actually do anything with the segment itself since this is all |
| // the job of the type system to figure it out but we can resolve any |
| // generic arguments used |
| if (segment.has_generic_args ()) |
| ResolveGenericArgs::go (segment.get_generic_args ()); |
| } |
| |
| // cannot fully resolve a qualified path as it is dependant on associated |
| // items |
| return UNKNOWN_NODEID; |
| } |
| |
| NodeId |
| ResolvePath::resolve_path (AST::SimplePath &expr) |
| { |
| NodeId crate_scope_id = resolver->peek_crate_module_scope (); |
| NodeId module_scope_id = resolver->peek_current_module_scope (); |
| |
| NodeId previous_resolved_node_id = UNKNOWN_NODEID; |
| NodeId resolved_node_id = UNKNOWN_NODEID; |
| for (size_t i = 0; i < expr.get_segments ().size (); i++) |
| { |
| AST::SimplePathSegment &segment = expr.get_segments ().at (i); |
| bool is_first_segment = i == 0; |
| bool is_final_segment = i >= (expr.get_segments ().size () - 1); |
| resolved_node_id = UNKNOWN_NODEID; |
| |
| if (segment.is_crate_path_seg ()) |
| { |
| // what is the current crate scope node id? |
| module_scope_id = crate_scope_id; |
| previous_resolved_node_id = module_scope_id; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_name (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == module_scope_id); |
| else |
| resolver->insert_resolved_name (segment.get_node_id (), |
| module_scope_id); |
| resolved_node_id = module_scope_id; |
| |
| continue; |
| } |
| else if (segment.is_super_path_seg ()) |
| { |
| if (!is_first_segment) |
| { |
| rust_error_at ( |
| segment.get_locus (), ErrorCode::E0433, |
| "%<super%> in paths can only be used in start position"); |
| return UNKNOWN_NODEID; |
| } |
| if (module_scope_id == crate_scope_id) |
| { |
| rust_error_at (segment.get_locus (), |
| "cannot use %<super%> at the crate scope"); |
| return UNKNOWN_NODEID; |
| } |
| |
| module_scope_id = resolver->peek_parent_module_scope (); |
| previous_resolved_node_id = module_scope_id; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_name (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == module_scope_id); |
| else |
| resolver->insert_resolved_name (segment.get_node_id (), |
| module_scope_id); |
| resolved_node_id = module_scope_id; |
| |
| continue; |
| } |
| |
| tl::optional<CanonicalPath &> resolved_child |
| = mappings.lookup_module_child (module_scope_id, |
| segment.get_segment_name ()); |
| if (resolved_child.has_value ()) |
| { |
| NodeId resolved_node = resolved_child->get_node_id (); |
| if (resolver->get_name_scope ().decl_was_declared_here ( |
| resolved_node)) |
| { |
| resolved_node_id = resolved_node; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_name (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node); |
| else |
| resolver->insert_resolved_name (segment.get_node_id (), |
| resolved_node); |
| } |
| else if (resolver->get_type_scope ().decl_was_declared_here ( |
| resolved_node)) |
| { |
| resolved_node_id = resolved_node; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_type (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node); |
| else |
| resolver->insert_resolved_type (segment.get_node_id (), |
| resolved_node); |
| } |
| else |
| { |
| rust_error_at (segment.get_locus (), |
| "Cannot find path %<%s%> in this scope", |
| segment.as_string ().c_str ()); |
| return UNKNOWN_NODEID; |
| } |
| } |
| |
| if (resolved_node_id == UNKNOWN_NODEID && is_first_segment) |
| { |
| // name scope first |
| NodeId resolved_node = UNKNOWN_NODEID; |
| const CanonicalPath path |
| = CanonicalPath::new_seg (segment.get_node_id (), |
| segment.get_segment_name ()); |
| if (resolver->get_name_scope ().lookup (path, &resolved_node)) |
| { |
| resolved_node_id = resolved_node; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_name (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node); |
| else |
| resolver->insert_resolved_name (segment.get_node_id (), |
| resolved_node); |
| } |
| // check the type scope |
| else if (resolver->get_type_scope ().lookup (path, &resolved_node)) |
| { |
| resolved_node_id = resolved_node; |
| |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok = resolver->lookup_resolved_type (segment.get_node_id (), |
| &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node); |
| else |
| resolver->insert_resolved_type (segment.get_node_id (), |
| resolved_node); |
| } |
| } |
| |
| // if we still have not resolved and this is the final segment and the |
| // final segment is self its likely the case: pub use |
| // |
| // result::Result::{self, Err, Ok}; |
| // |
| // Then the resolved_node_id is just the previous one so long as it is a |
| // resolved node id |
| // rust_debug_loc (segment.get_locus (), |
| // "trying to resolve seg: [%s] first [%s] last [%s]", |
| // segment.get_segment_name ().c_str (), |
| // is_first_segment ? "true" : "false", |
| // is_final_segment ? "true" : "false"); |
| if (resolved_node_id == UNKNOWN_NODEID && !is_first_segment |
| && is_final_segment && segment.is_lower_self_seg ()) |
| resolved_node_id = previous_resolved_node_id; |
| |
| // final check |
| if (resolved_node_id == UNKNOWN_NODEID) |
| { |
| rust_error_at (segment.get_locus (), |
| "cannot find simple path segment %<%s%> in this scope", |
| segment.as_string ().c_str ()); |
| return UNKNOWN_NODEID; |
| } |
| |
| if (mappings.node_is_module (resolved_node_id)) |
| { |
| module_scope_id = resolved_node_id; |
| } |
| |
| previous_resolved_node_id = resolved_node_id; |
| } |
| |
| resolved_node = resolved_node_id; |
| if (resolved_node_id != UNKNOWN_NODEID) |
| { |
| // name scope first |
| if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id)) |
| { |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok |
| = resolver->lookup_resolved_name (expr.get_node_id (), &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node_id); |
| else |
| resolver->insert_resolved_name (expr.get_node_id (), |
| resolved_node_id); |
| } |
| // check the type scope |
| else if (resolver->get_type_scope ().decl_was_declared_here ( |
| resolved_node_id)) |
| { |
| NodeId existing = UNKNOWN_NODEID; |
| bool ok |
| = resolver->lookup_resolved_type (expr.get_node_id (), &existing); |
| |
| if (ok) |
| rust_assert (existing == resolved_node_id); |
| else |
| resolver->insert_resolved_type (expr.get_node_id (), |
| resolved_node_id); |
| } |
| else |
| { |
| rust_unreachable (); |
| } |
| } |
| return resolved_node_id; |
| } |
| |
| } // namespace Resolver |
| } // namespace Rust |