// Copyright (C) 2020-2022 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-path.h" #include "rust-ast-resolve-type.h" #include "rust-path.h" namespace Rust { namespace Resolver { void ResolvePath::go (AST::PathInExpression *expr, NodeId parent) { ResolvePath resolver (parent); resolver.resolve_path (expr); } void ResolvePath::go (AST::QualifiedPathInExpression *expr, NodeId parent) { ResolvePath resolver (parent); resolver.resolve_path (expr); } void ResolvePath::go (AST::SimplePath *expr, NodeId parent) { ResolvePath resolver (parent); resolver.resolve_path (expr); } void ResolvePath::resolve_path (AST::PathInExpression *expr) { // resolve root segment first then apply segments in turn std::vector &segs = expr->get_segments (); AST::PathExprSegment &root_segment = segs.at (0); AST::PathIdentSegment &root_ident_seg = root_segment.get_ident_segment (); bool segment_is_type = false; CanonicalPath root_seg_path = CanonicalPath::new_seg (root_segment.get_node_id (), root_ident_seg.as_string ()); // name scope first if (resolver->get_name_scope ().lookup (root_seg_path, &resolved_node)) { segment_is_type = false; resolver->insert_resolved_name (root_segment.get_node_id (), resolved_node); resolver->insert_new_definition (root_segment.get_node_id (), Definition{expr->get_node_id (), parent}); } // check the type scope else if (resolver->get_type_scope ().lookup (root_seg_path, &resolved_node)) { segment_is_type = true; resolver->insert_resolved_type (root_segment.get_node_id (), resolved_node); resolver->insert_new_definition (root_segment.get_node_id (), Definition{expr->get_node_id (), parent}); } else { rust_error_at (expr->get_locus (), "Cannot find path %<%s%> in this scope", root_segment.as_string ().c_str ()); return; } if (root_segment.has_generic_args ()) { bool ok = ResolveTypeToCanonicalPath::type_resolve_generic_args ( root_segment.get_generic_args ()); if (!ok) { rust_error_at (root_segment.get_locus (), "failed to resolve generic arguments"); return; } } bool is_single_segment = segs.size () == 1; if (is_single_segment) { if (segment_is_type) resolver->insert_resolved_type (expr->get_node_id (), resolved_node); else resolver->insert_resolved_name (expr->get_node_id (), resolved_node); resolver->insert_new_definition (expr->get_node_id (), Definition{expr->get_node_id (), parent}); return; } resolve_segments (root_seg_path, 1, expr->get_segments (), expr->get_node_id (), expr->get_locus ()); } void ResolvePath::resolve_path (AST::QualifiedPathInExpression *expr) { AST::QualifiedPathType &root_segment = expr->get_qualified_path_type (); bool canonicalize_type_with_generics = false; ResolveType::go (&root_segment.get_as_type_path (), root_segment.get_node_id (), canonicalize_type_with_generics); ResolveType::go (root_segment.get_type ().get (), root_segment.get_node_id (), canonicalize_type_with_generics); bool type_resolve_generic_args = true; CanonicalPath impl_type_seg = ResolveTypeToCanonicalPath::resolve (*root_segment.get_type ().get (), canonicalize_type_with_generics, type_resolve_generic_args); CanonicalPath trait_type_seg = ResolveTypeToCanonicalPath::resolve (root_segment.get_as_type_path (), canonicalize_type_with_generics, type_resolve_generic_args); CanonicalPath root_seg_path = TraitImplProjection::resolve (root_segment.get_node_id (), trait_type_seg, impl_type_seg); bool segment_is_type = false; // name scope first if (resolver->get_name_scope ().lookup (root_seg_path, &resolved_node)) { segment_is_type = false; resolver->insert_resolved_name (root_segment.get_node_id (), resolved_node); resolver->insert_new_definition (root_segment.get_node_id (), Definition{expr->get_node_id (), parent}); } // check the type scope else if (resolver->get_type_scope ().lookup (root_seg_path, &resolved_node)) { segment_is_type = true; resolver->insert_resolved_type (root_segment.get_node_id (), resolved_node); resolver->insert_new_definition (root_segment.get_node_id (), Definition{expr->get_node_id (), parent}); } else { rust_error_at (expr->get_locus (), "Cannot find path %<%s%> in this scope", root_segment.as_string ().c_str ()); return; } bool is_single_segment = expr->get_segments ().empty (); if (is_single_segment) { if (segment_is_type) resolver->insert_resolved_type (expr->get_node_id (), resolved_node); else resolver->insert_resolved_name (expr->get_node_id (), resolved_node); resolver->insert_new_definition (expr->get_node_id (), Definition{expr->get_node_id (), parent}); return; } resolve_segments (root_seg_path, 0, expr->get_segments (), expr->get_node_id (), expr->get_locus ()); } void ResolvePath::resolve_segments (CanonicalPath prefix, size_t offs, std::vector &segs, NodeId expr_node_id, Location expr_locus) { // we can attempt to resolve this path fully CanonicalPath path = prefix; bool segment_is_type = false; for (size_t i = offs; i < segs.size (); i++) { AST::PathExprSegment &seg = segs.at (i); auto s = ResolvePathSegmentToCanonicalPath::resolve (seg); path = path.append (s); // reset state segment_is_type = false; resolved_node = UNKNOWN_NODEID; if (resolver->get_name_scope ().lookup (path, &resolved_node)) { resolver->insert_resolved_name (seg.get_node_id (), resolved_node); resolver->insert_new_definition (seg.get_node_id (), Definition{expr_node_id, parent}); } // check the type scope else if (resolver->get_type_scope ().lookup (path, &resolved_node)) { segment_is_type = true; resolver->insert_resolved_type (seg.get_node_id (), resolved_node); resolver->insert_new_definition (seg.get_node_id (), Definition{expr_node_id, parent}); } else { // attempt to fully resolve the path which is allowed to fail given // the following scenario // // https://github.com/Rust-GCC/gccrs/issues/355 Paths are // resolved fully here, there are limitations though imagine: // // struct Foo (A); // // impl Foo { // fn test() -> ... // // impl Foo { // fn test() -> ... // // fn main() { // let a:i32 = Foo::test(); // // there are multiple paths that test can resolve to Foo::::test // here so we cannot resolve this case // // canonical names: // // struct Foo -> Foo // impl Foo::fn test -> Foo::isize::test // impl Foo::fn test -> Foo::f32::test // // Since there is the case we have the following paths for test: // // Foo::isize::test // Foo::f32::test // vs // Foo::test // // but the lookup was simply Foo::test we must rely on type resolution // to figure this type out in a similar fashion to method resolution // with a probe phase // nothing more we can do we need the type resolver to try and resolve // this return; } } // its fully resolved lets mark it as such if (resolved_node != UNKNOWN_NODEID) { if (segment_is_type) resolver->insert_resolved_type (expr_node_id, resolved_node); else resolver->insert_resolved_name (expr_node_id, resolved_node); resolver->insert_new_definition (expr_node_id, Definition{expr_node_id, parent}); } } static bool lookup_and_insert_segment (Resolver *resolver, CanonicalPath path, NodeId segment_id, NodeId *to_resolve, bool &is_type) { if (resolver->get_name_scope ().lookup (path, to_resolve)) { resolver->insert_resolved_name (segment_id, *to_resolve); } else if (resolver->get_type_scope ().lookup (path, to_resolve)) { is_type = true; resolver->insert_resolved_type (segment_id, *to_resolve); } else { return false; } return true; } void ResolvePath::resolve_path (AST::SimplePath *simple_path) { // resolve root segment first then apply segments in turn auto expr_node_id = simple_path->get_node_id (); auto is_type = false; auto path = CanonicalPath::create_empty (); for (const auto &seg : simple_path->get_segments ()) { auto s = ResolveSimplePathSegmentToCanonicalPath::resolve (seg); path = path.append (s); // Reset state resolved_node = UNKNOWN_NODEID; is_type = false; if (!lookup_and_insert_segment (resolver, path, seg.get_node_id (), &resolved_node, is_type)) { rust_error_at (seg.get_locus (), "cannot find simple path segment %qs", seg.as_string ().c_str ()); return; } } if (resolved_node == UNKNOWN_NODEID) { rust_error_at (simple_path->get_locus (), "could not resolve simple path %qs", simple_path->as_string ().c_str ()); return; } if (is_type) resolver->insert_resolved_type (expr_node_id, resolved_node); else resolver->insert_resolved_name (expr_node_id, resolved_node); resolver->insert_new_definition (expr_node_id, Definition{expr_node_id, parent}); } } // namespace Resolver } // namespace Rust