| // 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-type.h" |
| #include "rust-ast-resolve-expr.h" |
| |
| namespace Rust { |
| namespace Resolver { |
| |
| // rust-ast-resolve-type.h |
| |
| void |
| ResolveType::visit (AST::ArrayType &type) |
| { |
| type.get_elem_type ()->accept_vis (*this); |
| ResolveExpr::go (type.get_size_expr ().get (), CanonicalPath::create_empty (), |
| CanonicalPath::create_empty ()); |
| } |
| |
| void |
| ResolveType::visit (AST::TraitObjectTypeOneBound &type) |
| { |
| ResolveTypeBound::go (&type.get_trait_bound ()); |
| } |
| |
| void |
| ResolveType::visit (AST::TraitObjectType &type) |
| { |
| for (auto &bound : type.get_type_param_bounds ()) |
| { |
| /* NodeId bound_resolved_id = */ |
| ResolveTypeBound::go (bound.get ()); |
| } |
| } |
| |
| void |
| ResolveType::visit (AST::ReferenceType &type) |
| { |
| resolved_node = ResolveType::go (type.get_type_referenced ().get ()); |
| } |
| |
| void |
| ResolveType::visit (AST::RawPointerType &type) |
| { |
| resolved_node = ResolveType::go (type.get_type_pointed_to ().get ()); |
| } |
| |
| void |
| ResolveType::visit (AST::InferredType &) |
| { |
| // FIXME |
| } |
| |
| void |
| ResolveType::visit (AST::NeverType &) |
| { |
| // FIXME |
| } |
| |
| void |
| ResolveType::visit (AST::SliceType &type) |
| { |
| resolved_node = ResolveType::go (type.get_elem_type ().get ()); |
| } |
| |
| // resolve relative type-paths |
| |
| bool |
| ResolveRelativeTypePath::go (AST::TypePath &path, NodeId &resolved_node_id) |
| { |
| auto resolver = Resolver::get (); |
| auto mappings = Analysis::Mappings::get (); |
| |
| NodeId module_scope_id = resolver->peek_current_module_scope (); |
| NodeId previous_resolved_node_id = module_scope_id; |
| for (size_t i = 0; i < path.get_segments ().size (); i++) |
| { |
| auto &segment = path.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, |
| "failed to resolve: %<%s%> in paths can only be used " |
| "in start position", |
| segment->as_string ().c_str ()); |
| return false; |
| } |
| |
| 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; |
| 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 false; |
| } |
| |
| module_scope_id = resolver->peek_parent_module_scope (); |
| previous_resolved_node_id = module_scope_id; |
| resolver->insert_resolved_name (segment->get_node_id (), |
| module_scope_id); |
| continue; |
| } |
| |
| switch (segment->get_type ()) |
| { |
| case AST::TypePathSegment::SegmentType::GENERIC: { |
| AST::TypePathSegmentGeneric *s |
| = static_cast<AST::TypePathSegmentGeneric *> (segment.get ()); |
| if (s->has_generic_args ()) |
| ResolveGenericArgs::go (s->get_generic_args ()); |
| } |
| break; |
| |
| case AST::TypePathSegment::SegmentType::REG: |
| // nothing to do |
| break; |
| |
| case AST::TypePathSegment::SegmentType::FUNCTION: |
| AST::TypePathSegmentFunction *fnseg |
| = static_cast<AST::TypePathSegmentFunction *> (segment.get ()); |
| |
| AST::TypePathFunction &fn = fnseg->get_type_path_function (); |
| for (auto ¶m : fn.get_params ()) |
| { |
| ResolveType::go (param.get ()); |
| } |
| |
| if (fn.has_return_type ()) |
| { |
| ResolveType::go (fn.get_return_type ().get ()); |
| } |
| |
| break; |
| } |
| |
| 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_type_scope ().lookup (path, &resolved_node)) |
| { |
| resolver->insert_resolved_type (segment->get_node_id (), |
| resolved_node); |
| resolved_node_id = resolved_node; |
| } |
| else if (resolver->get_name_scope ().lookup (path, &resolved_node)) |
| { |
| resolver->insert_resolved_name (segment->get_node_id (), |
| resolved_node); |
| resolved_node_id = resolved_node; |
| } |
| else if (segment->is_lower_self_seg ()) |
| { |
| // what is the current crate scope node id? |
| module_scope_id = crate_scope_id; |
| previous_resolved_node_id = module_scope_id; |
| resolver->insert_resolved_name (segment->get_node_id (), |
| module_scope_id); |
| |
| continue; |
| } |
| } |
| |
| 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; |
| 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; |
| 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 false; |
| } |
| } |
| } |
| |
| 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::E0412, |
| "failed to resolve TypePath: %s in this scope", |
| segment->as_string ().c_str ()); |
| return false; |
| } |
| } |
| |
| if (resolved_node_id != UNKNOWN_NODEID) |
| { |
| // name scope first |
| if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id)) |
| { |
| resolver->insert_resolved_name (path.get_node_id (), |
| resolved_node_id); |
| } |
| // check the type scope |
| else if (resolver->get_type_scope ().decl_was_declared_here ( |
| resolved_node_id)) |
| { |
| resolver->insert_resolved_type (path.get_node_id (), |
| resolved_node_id); |
| } |
| else |
| { |
| rust_unreachable (); |
| } |
| } |
| |
| return true; |
| } |
| |
| // qualified type paths |
| |
| ResolveRelativeQualTypePath::ResolveRelativeQualTypePath () |
| : failure_flag (false) |
| {} |
| |
| bool |
| ResolveRelativeQualTypePath::go (AST::QualifiedPathInType &path) |
| { |
| ResolveRelativeQualTypePath o; |
| |
| // resolve the type and trait path |
| auto &qualified_path = path.get_qualified_path_type (); |
| if (!o.resolve_qual_seg (qualified_path)) |
| return false; |
| |
| // qualified types are similar to other paths in that we cannot guarantee |
| // that we can resolve the path at name resolution. We must look up |
| // associated types and type information to figure this out properly |
| |
| std::unique_ptr<AST::TypePathSegment> &associated |
| = path.get_associated_segment (); |
| |
| associated->accept_vis (o); |
| if (o.failure_flag) |
| return false; |
| |
| for (auto &seg : path.get_segments ()) |
| { |
| seg->accept_vis (o); |
| if (o.failure_flag) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool |
| ResolveRelativeQualTypePath::resolve_qual_seg (AST::QualifiedPathType &seg) |
| { |
| if (seg.is_error ()) |
| { |
| rust_error_at (seg.get_locus (), "segment has error: %s", |
| seg.as_string ().c_str ()); |
| return false; |
| } |
| |
| auto type = seg.get_type ().get (); |
| ResolveType::go (type); |
| |
| if (seg.has_as_clause ()) |
| ResolveType::go (&seg.get_as_type_path ()); |
| |
| return true; |
| } |
| |
| void |
| ResolveRelativeQualTypePath::visit (AST::TypePathSegmentGeneric &seg) |
| { |
| if (seg.is_error ()) |
| { |
| failure_flag = true; |
| rust_error_at (seg.get_locus (), "segment has error: %s", |
| seg.as_string ().c_str ()); |
| return; |
| } |
| |
| ResolveGenericArgs::go (seg.get_generic_args ()); |
| } |
| |
| void |
| ResolveRelativeQualTypePath::visit (AST::TypePathSegment &seg) |
| { |
| if (seg.is_error ()) |
| { |
| failure_flag = true; |
| rust_error_at (seg.get_locus (), "segment has error: %s", |
| seg.as_string ().c_str ()); |
| return; |
| } |
| } |
| |
| // resolve to canonical path |
| |
| bool |
| ResolveTypeToCanonicalPath::go (AST::Type *type, CanonicalPath &result) |
| { |
| ResolveTypeToCanonicalPath resolver; |
| type->accept_vis (resolver); |
| result = resolver.result; |
| return !resolver.result.is_empty (); |
| } |
| |
| void |
| ResolveTypeToCanonicalPath::visit (AST::TypePath &path) |
| { |
| NodeId resolved_node = UNKNOWN_NODEID; |
| if (!resolver->lookup_resolved_name (path.get_node_id (), &resolved_node)) |
| { |
| resolver->lookup_resolved_type (path.get_node_id (), &resolved_node); |
| } |
| |
| if (resolved_node == UNKNOWN_NODEID) |
| return; |
| |
| const CanonicalPath *type_path = nullptr; |
| if (mappings->lookup_canonical_path (resolved_node, &type_path)) |
| { |
| auto &final_seg = path.get_segments ().back (); |
| switch (final_seg->get_type ()) |
| { |
| case AST::TypePathSegment::SegmentType::GENERIC: { |
| AST::TypePathSegmentGeneric *s |
| = static_cast<AST::TypePathSegmentGeneric *> (final_seg.get ()); |
| |
| std::vector<CanonicalPath> args; |
| if (s->has_generic_args ()) |
| { |
| ResolveGenericArgs::go (s->get_generic_args ()); |
| for (auto &generic : s->get_generic_args ().get_generic_args ()) |
| { |
| // FIXME: What do we want to do here in case there is a |
| // constant or an ambiguous const generic? |
| // TODO: At that point, will all generics have been |
| // disambiguated? Can we thus canonical resolve types and |
| // const and `rust_unreachable` on ambiguous types? |
| // This is probably fine as we just want to canonicalize |
| // types, right? |
| if (generic.get_kind () == AST::GenericArg::Kind::Type) |
| { |
| CanonicalPath arg = CanonicalPath::create_empty (); |
| bool ok = ResolveTypeToCanonicalPath::go ( |
| generic.get_type ().get (), arg); |
| if (ok) |
| args.push_back (std::move (arg)); |
| } |
| } |
| } |
| |
| result = *type_path; |
| if (!args.empty ()) |
| { |
| // append this onto the path |
| std::string buf; |
| for (size_t i = 0; i < args.size (); i++) |
| { |
| bool has_next = (i + 1) < args.size (); |
| const auto &arg = args.at (i); |
| |
| buf += arg.get (); |
| if (has_next) |
| buf += ", "; |
| } |
| |
| std::string arg_seg = "<" + buf + ">"; |
| CanonicalPath argument_seg |
| = CanonicalPath::new_seg (s->get_node_id (), arg_seg); |
| result = result.append (argument_seg); |
| } |
| } |
| break; |
| |
| default: |
| result = *type_path; |
| break; |
| } |
| } |
| } |
| |
| void |
| ResolveTypeToCanonicalPath::visit (AST::ReferenceType &type) |
| { |
| CanonicalPath path = CanonicalPath::create_empty (); |
| bool ok |
| = ResolveTypeToCanonicalPath::go (type.get_type_referenced ().get (), path); |
| if (ok) |
| { |
| std::string ref_type_str = type.is_mut () ? "mut" : ""; |
| std::string ref_path = "&" + ref_type_str + " " + path.get (); |
| result = CanonicalPath::new_seg (type.get_node_id (), ref_path); |
| } |
| } |
| |
| void |
| ResolveTypeToCanonicalPath::visit (AST::RawPointerType &type) |
| { |
| CanonicalPath path = CanonicalPath::create_empty (); |
| bool ok |
| = ResolveTypeToCanonicalPath::go (type.get_type_pointed_to ().get (), path); |
| if (ok) |
| { |
| std::string ptr_type_str |
| = type.get_pointer_type () == AST::RawPointerType::CONST ? "const" |
| : "mut"; |
| std::string ptr_path = "*" + ptr_type_str + " " + path.get (); |
| result = CanonicalPath::new_seg (type.get_node_id (), ptr_path); |
| } |
| } |
| |
| void |
| ResolveTypeToCanonicalPath::visit (AST::SliceType &type) |
| { |
| CanonicalPath path = CanonicalPath::create_empty (); |
| bool ok = ResolveTypeToCanonicalPath::go (type.get_elem_type ().get (), path); |
| if (ok) |
| { |
| std::string slice_path = "[" + path.get () + "]"; |
| result = CanonicalPath::new_seg (type.get_node_id (), slice_path); |
| } |
| } |
| |
| void |
| ResolveTypeToCanonicalPath::visit (AST::TraitObjectTypeOneBound &type) |
| { |
| CanonicalPath path = CanonicalPath::create_empty (); |
| bool ok |
| = ResolveTypeToCanonicalPath::go (&type.get_trait_bound ().get_type_path (), |
| path); |
| if (ok) |
| { |
| std::string slice_path = "<dyn " + path.get () + ">"; |
| result = CanonicalPath::new_seg (type.get_node_id (), slice_path); |
| } |
| } |
| |
| void |
| ResolveTypeToCanonicalPath::visit (AST::TraitObjectType &) |
| { |
| // FIXME is this actually allowed? dyn A+B |
| rust_unreachable (); |
| } |
| |
| ResolveTypeToCanonicalPath::ResolveTypeToCanonicalPath () |
| : ResolverBase (), result (CanonicalPath::create_empty ()) |
| {} |
| |
| bool |
| ResolveGenericArgs::is_const_value_name (const CanonicalPath &path) |
| { |
| NodeId resolved; |
| auto found = resolver->get_name_scope ().lookup (path, &resolved); |
| |
| return found; |
| } |
| |
| bool |
| ResolveGenericArgs::is_type_name (const CanonicalPath &path) |
| { |
| NodeId resolved; |
| auto found = resolver->get_type_scope ().lookup (path, &resolved); |
| |
| return found; |
| } |
| |
| void |
| ResolveGenericArgs::disambiguate (AST::GenericArg &arg) |
| { |
| auto path = canonical_prefix.append ( |
| CanonicalPath::new_seg (UNKNOWN_NODEID, arg.get_path ())); |
| |
| auto is_type = is_type_name (path); |
| auto is_value = is_const_value_name (path); |
| |
| // In case we cannot find anything, we resolve the ambiguity to a type. |
| // This causes the typechecker to error out properly and when necessary. |
| // But types also take priority over const values in the case of |
| // ambiguities, hence the weird control flow |
| if (is_type || (!is_type && !is_value)) |
| arg = arg.disambiguate_to_type (); |
| else if (is_value) |
| arg = arg.disambiguate_to_const (); |
| } |
| |
| void |
| ResolveGenericArgs::resolve_disambiguated_generic (AST::GenericArg &arg) |
| { |
| switch (arg.get_kind ()) |
| { |
| case AST::GenericArg::Kind::Const: |
| ResolveExpr::go (arg.get_expression ().get (), prefix, canonical_prefix); |
| break; |
| case AST::GenericArg::Kind::Type: |
| ResolveType::go (arg.get_type ().get ()); |
| break; |
| default: |
| rust_unreachable (); |
| } |
| } |
| void |
| ResolveGenericArgs::go (AST::GenericArgs &generic_args) |
| { |
| auto empty = CanonicalPath::create_empty (); |
| |
| go (generic_args, empty, empty); |
| } |
| |
| void |
| ResolveGenericArgs::go (AST::GenericArgs &generic_args, |
| const CanonicalPath &prefix, |
| const CanonicalPath &canonical_prefix) |
| { |
| auto resolver = ResolveGenericArgs (prefix, canonical_prefix); |
| |
| for (auto &arg : generic_args.get_generic_args ()) |
| { |
| if (arg.get_kind () == AST::GenericArg::Kind::Either) |
| resolver.disambiguate (arg); |
| |
| resolver.resolve_disambiguated_generic (arg); |
| } |
| |
| for (auto &binding : generic_args.get_binding_args ()) |
| { |
| ResolveType::go (binding.get_type ().get ()); |
| } |
| } |
| |
| } // namespace Resolver |
| } // namespace Rust |