// Copyright (C) 2020-2023 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 // . #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 ()) { // error[E0433]: failed to resolve: `self` in paths can only be used // in start position rust_error_at (segment->get_locus (), "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 (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 (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) { Optional resolved_child = mappings->lookup_module_child (module_scope_id, ident_seg.as_string ()); if (resolved_child.is_some ()) { 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 (), "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 { gcc_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 &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 (); NodeId type_resolved_node = ResolveType::go (type); if (type_resolved_node == UNKNOWN_NODEID) return false; if (!seg.has_as_clause ()) return true; NodeId trait_resolved_node = ResolveType::go (&seg.get_as_type_path ()); if (trait_resolved_node == UNKNOWN_NODEID) return false; 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 (final_seg.get ()); std::vector 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 `gcc_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 = ""; result = CanonicalPath::new_seg (type.get_node_id (), slice_path); } } void ResolveTypeToCanonicalPath::visit (AST::TraitObjectType &) { // FIXME is this actually allowed? dyn A+B gcc_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: gcc_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