// Copyright (C) 2020 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.h" #include "rust-ast-full.h" #include "rust-tyty.h" #include "rust-ast-resolve-toplevel.h" #include "rust-ast-resolve-item.h" #include "rust-ast-resolve-expr.h" #define MKBUILTIN_TYPE(_X, _R, _TY) \ do \ { \ AST::PathIdentSegment seg (_X); \ auto typePath = ::std::unique_ptr ( \ new AST::TypePathSegment (::std::move (seg), false, \ Linemap::predeclared_location ())); \ ::std::vector< ::std::unique_ptr > segs; \ segs.push_back (::std::move (typePath)); \ auto builtin_type \ = new AST::TypePath (::std::move (segs), \ Linemap::predeclared_location (), false); \ _R.push_back (builtin_type); \ tyctx->insert_builtin (_TY->get_ref (), builtin_type->get_node_id (), \ _TY); \ } \ while (0) extern bool saw_errors (void); namespace Rust { namespace Resolver { // Resolver Resolver::Resolver () : mappings (Analysis::Mappings::get ()), tyctx (TypeCheckContext::get ()), name_scope (Scope (mappings->get_current_crate ())), type_scope (Scope (mappings->get_current_crate ())), label_scope (Scope (mappings->get_current_crate ())), global_type_node_id (UNKNOWN_NODEID), unit_ty_node_id (UNKNOWN_NODEID) { generate_builtins (); } Resolver * Resolver::get () { static Resolver *instance; if (instance == nullptr) instance = new Resolver (); return instance; } void Resolver::push_new_name_rib (Rib *r) { rust_assert (name_ribs.find (r->get_node_id ()) == name_ribs.end ()); name_ribs[r->get_node_id ()] = r; } void Resolver::push_new_type_rib (Rib *r) { if (type_ribs.size () == 0) global_type_node_id = r->get_node_id (); rust_assert (type_ribs.find (r->get_node_id ()) == type_ribs.end ()); type_ribs[r->get_node_id ()] = r; } void Resolver::push_new_label_rib (Rib *r) { rust_assert (label_ribs.find (r->get_node_id ()) == label_ribs.end ()); label_ribs[r->get_node_id ()] = r; } bool Resolver::find_name_rib (NodeId id, Rib **rib) { auto it = name_ribs.find (id); if (it == name_ribs.end ()) return false; *rib = it->second; return true; } bool Resolver::find_type_rib (NodeId id, Rib **rib) { auto it = type_ribs.find (id); if (it == type_ribs.end ()) return false; *rib = it->second; return true; } void Resolver::insert_builtin_types (Rib *r) { auto builtins = get_builtin_types (); for (auto &builtin : builtins) { CanonicalPath builtin_path = CanonicalPath::new_seg (builtin->get_node_id (), builtin->as_string ()); r->insert_name (builtin_path, builtin->get_node_id (), Linemap::predeclared_location (), false, [] (const CanonicalPath &, NodeId, Location) -> void {}); } } std::vector & Resolver::get_builtin_types () { return builtins; } void Resolver::generate_builtins () { auto u8 = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U8); auto u16 = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U16); auto u32 = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U32); auto u64 = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U64); auto u128 = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U128); auto i8 = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I8); auto i16 = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I16); auto i32 = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I32); auto i64 = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I64); auto i128 = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I128); auto rbool = new TyTy::BoolType (mappings->get_next_hir_id ()); auto f32 = new TyTy::FloatType (mappings->get_next_hir_id (), TyTy::FloatType::F32); auto f64 = new TyTy::FloatType (mappings->get_next_hir_id (), TyTy::FloatType::F64); auto usize = new TyTy::USizeType (mappings->get_next_hir_id ()); auto isize = new TyTy::ISizeType (mappings->get_next_hir_id ()); auto char_tyty = new TyTy::CharType (mappings->get_next_hir_id ()); auto str = new TyTy::StrType (mappings->get_next_hir_id ()); MKBUILTIN_TYPE ("u8", builtins, u8); MKBUILTIN_TYPE ("u16", builtins, u16); MKBUILTIN_TYPE ("u32", builtins, u32); MKBUILTIN_TYPE ("u64", builtins, u64); MKBUILTIN_TYPE ("u128", builtins, u128); MKBUILTIN_TYPE ("i8", builtins, i8); MKBUILTIN_TYPE ("i16", builtins, i16); MKBUILTIN_TYPE ("i32", builtins, i32); MKBUILTIN_TYPE ("i64", builtins, i64); MKBUILTIN_TYPE ("i128", builtins, i128); MKBUILTIN_TYPE ("bool", builtins, rbool); MKBUILTIN_TYPE ("f32", builtins, f32); MKBUILTIN_TYPE ("f64", builtins, f64); MKBUILTIN_TYPE ("usize", builtins, usize); MKBUILTIN_TYPE ("isize", builtins, isize); MKBUILTIN_TYPE ("char", builtins, char_tyty); MKBUILTIN_TYPE ("str", builtins, str); // unit type () TyTy::TupleType *unit_tyty = new TyTy::TupleType (mappings->get_next_hir_id ()); std::vector > elems; AST::TupleType *unit_type = new AST::TupleType (std::move (elems), Linemap::predeclared_location ()); builtins.push_back (unit_type); tyctx->insert_builtin (unit_tyty->get_ref (), unit_type->get_node_id (), unit_tyty); set_unit_type_node_id (unit_type->get_node_id ()); } void Resolver::insert_new_definition (NodeId id, Definition def) { auto it = name_definitions.find (id); if (it != name_definitions.end ()) { rust_assert (it->second.is_equal (def)); return; } name_definitions[id] = def; } bool Resolver::lookup_definition (NodeId id, Definition *def) { auto it = name_definitions.find (id); if (it == name_definitions.end ()) return false; *def = it->second; return true; } void Resolver::insert_resolved_name (NodeId refId, NodeId defId) { resolved_names[refId] = defId; get_name_scope ().append_reference_for_def (refId, defId); } bool Resolver::lookup_resolved_name (NodeId refId, NodeId *defId) { auto it = resolved_names.find (refId); if (it == resolved_names.end ()) return false; *defId = it->second; return true; } void Resolver::insert_resolved_type (NodeId refId, NodeId defId) { // auto it = resolved_types.find (refId); // rust_assert (it == resolved_types.end ()); resolved_types[refId] = defId; get_type_scope ().append_reference_for_def (refId, defId); } bool Resolver::lookup_resolved_type (NodeId refId, NodeId *defId) { auto it = resolved_types.find (refId); if (it == resolved_types.end ()) return false; *defId = it->second; return true; } void Resolver::insert_resolved_label (NodeId refId, NodeId defId) { auto it = resolved_labels.find (refId); rust_assert (it == resolved_labels.end ()); resolved_labels[refId] = defId; get_label_scope ().append_reference_for_def (refId, defId); } bool Resolver::lookup_resolved_label (NodeId refId, NodeId *defId) { auto it = resolved_labels.find (refId); if (it == resolved_labels.end ()) return false; *defId = it->second; return true; } // NameResolution NameResolution * NameResolution::get () { static NameResolution *instance; if (instance == nullptr) instance = new NameResolution (); return instance; } NameResolution::NameResolution () : resolver (Resolver::get ()), mappings (Analysis::Mappings::get ()) { // these are global resolver->get_type_scope ().push (mappings->get_next_node_id ()); resolver->insert_builtin_types (resolver->get_type_scope ().peek ()); resolver->push_new_type_rib (resolver->get_type_scope ().peek ()); } void NameResolution::Resolve (AST::Crate &crate) { auto resolver = get (); resolver->go (crate); } void NameResolution::go (AST::Crate &crate) { // setup parent scoping for names resolver->get_name_scope ().push (crate.get_node_id ()); resolver->push_new_name_rib (resolver->get_name_scope ().peek ()); // setup parent scoping for new types resolver->get_type_scope ().push (mappings->get_next_node_id ()); resolver->push_new_type_rib (resolver->get_type_scope ().peek ()); // setup label scope resolver->get_label_scope ().push (mappings->get_next_node_id ()); resolver->push_new_label_rib (resolver->get_type_scope ().peek ()); // first gather the top-level namespace names then we drill down for (auto it = crate.items.begin (); it != crate.items.end (); it++) ResolveTopLevel::go (it->get ()); if (saw_errors ()) return; // next we can drill down into the items and their scopes for (auto it = crate.items.begin (); it != crate.items.end (); it++) ResolveItem::go (it->get ()); } // rust-ast-resolve-expr.h void ResolveExpr::visit (AST::BlockExpr &expr) { NodeId scope_node_id = expr.get_node_id (); resolver->get_name_scope ().push (scope_node_id); resolver->get_type_scope ().push (scope_node_id); resolver->get_label_scope ().push (scope_node_id); resolver->push_new_name_rib (resolver->get_name_scope ().peek ()); resolver->push_new_type_rib (resolver->get_type_scope ().peek ()); resolver->push_new_label_rib (resolver->get_type_scope ().peek ()); for (auto &s : expr.get_statements ()) ResolveStmt::go (s.get (), s->get_node_id ()); if (expr.has_tail_expr ()) ResolveExpr::go (expr.get_tail_expr ().get (), expr.get_node_id ()); resolver->get_name_scope ().pop (); resolver->get_type_scope ().pop (); resolver->get_label_scope ().pop (); } // rust-ast-resolve-struct-expr-field.h void ResolveStructExprField::visit (AST::StructExprFieldIdentifierValue &field) { ResolveExpr::go (field.get_value ().get (), field.get_node_id ()); } void ResolveStructExprField::visit (AST::StructExprFieldIndexValue &field) { ResolveExpr::go (field.get_value ().get (), field.get_node_id ()); } void ResolveStructExprField::visit (AST::StructExprFieldIdentifier &field) { AST::IdentifierExpr expr (field.get_field_name (), {}, field.get_locus ()); expr.set_node_id (field.get_node_id ()); ResolveExpr::go (&expr, field.get_node_id ()); } // rust-ast-resolve-type.h std::string ResolveTypeToCanonicalPath::canonicalize_generic_args (AST::GenericArgs &args) { std::string buf; size_t i = 0; size_t total = args.get_type_args ().size (); for (auto &ty_arg : args.get_type_args ()) { buf += ty_arg->as_string (); if ((i + 1) < total) buf += ","; i++; } return "<" + buf + ">"; } bool ResolveTypeToCanonicalPath::type_resolve_generic_args (AST::GenericArgs &args) { for (auto > : args.get_type_args ()) { ResolveType::go (gt.get (), UNKNOWN_NODEID); // FIXME error handling here for inference variable since they do not have // a node to resolve to // if (resolved == UNKNOWN_NODEID) return false; } return true; } void ResolveTypeToCanonicalPath::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; } if (!seg.has_generic_args ()) { result = CanonicalPath::new_seg (seg.get_node_id (), seg.get_ident_segment ().as_string ()); return; } if (type_resolve_generic_args_flag) { bool ok = type_resolve_generic_args (seg.get_generic_args ()); failure_flag = !ok; } if (include_generic_args_flag) { std::string generics = canonicalize_generic_args (seg.get_generic_args ()); result = CanonicalPath::new_seg (seg.get_node_id (), seg.get_ident_segment ().as_string () + "::" + generics); return; } result = CanonicalPath::new_seg (seg.get_node_id (), seg.get_ident_segment ().as_string ()); } void ResolveTypeToCanonicalPath::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; } CanonicalPath ident_seg = CanonicalPath::new_seg (seg.get_node_id (), seg.get_ident_segment ().as_string ()); result = result.append (ident_seg); } // rust-ast-resolve-expr.h 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}); } } // 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 (), type.get_node_id ()); } void ResolveType::visit (AST::TraitObjectTypeOneBound &type) { NodeId bound_resolved_id = ResolveTypeBound::go (&type.get_trait_bound (), type.get_node_id ()); ok = bound_resolved_id != UNKNOWN_NODEID; } // rust-ast-resolve-item.h void ResolveItem::resolve_impl_item (AST::TraitImplItem *item, const CanonicalPath &self) { ResolveImplItems::go (item, self); } void ResolveItem::resolve_impl_item (AST::InherentImplItem *item, const CanonicalPath &self) { ResolveImplItems::go (item, self); } void ResolveItem::resolve_extern_item (AST::ExternalItem *item) { ResolveExternItem::go (item); } // qualified path in type bool ResolveRelativeTypePath::resolve_qual_seg (AST::QualifiedPathType &seg, CanonicalPath &result) { if (seg.is_error ()) { rust_error_at (seg.get_locus (), "segment has error: %s", seg.as_string ().c_str ()); return false; } bool include_generic_args_in_path = false; NodeId type_resolved_node = ResolveType::go (seg.get_type ().get (), seg.get_node_id ()); if (type_resolved_node == UNKNOWN_NODEID) return false; CanonicalPath impl_type_seg = ResolveTypeToCanonicalPath::resolve (*seg.get_type ().get (), include_generic_args_in_path); if (!seg.has_as_clause ()) { result = result.append (impl_type_seg); return true; } NodeId trait_resolved_node = ResolveType::go (&seg.get_as_type_path (), seg.get_node_id ()); if (trait_resolved_node == UNKNOWN_NODEID) return false; CanonicalPath trait_type_seg = ResolveTypeToCanonicalPath::resolve (seg.get_as_type_path (), include_generic_args_in_path); CanonicalPath projection = TraitImplProjection::resolve (seg.get_node_id (), trait_type_seg, impl_type_seg); result = result.append (projection); return true; } } // namespace Resolver } // namespace Rust