blob: bbb05380d2a927ed1cada7b8731abf169bdc547a [file] [log] [blame]
// 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 &param : 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