// 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 // . #include "rust-name-resolver.h" #include "rust-ast-full.h" namespace Rust { namespace Resolver { Rib::Rib (CrateNum crateNum, NodeId node_id) : crate_num (crateNum), node_id (node_id) {} void Rib::insert_name ( const CanonicalPath &path, NodeId id, location_t locus, bool shadow, ItemType type, std::function dup_cb) { auto it = path_mappings.find (path); bool path_already_exists = it != path_mappings.end (); if (path_already_exists && !shadow) { const auto &decl = decls_within_rib.find (it->second); if (decl != decls_within_rib.end ()) dup_cb (path, it->second, decl->second); else dup_cb (path, it->second, locus); return; } path_mappings[path] = id; reverse_path_mappings.insert ({id, path}); decls_within_rib.insert ({id, locus}); references[id] = {}; decl_type_mappings.insert ({id, type}); } bool Rib::lookup_name (const CanonicalPath &ident, NodeId *id) { auto it = path_mappings.find (ident); if (it == path_mappings.end ()) return false; *id = it->second; return true; } void Rib::clear_name (const CanonicalPath &ident, NodeId id) { auto ii = path_mappings.find (ident); if (ii != path_mappings.end ()) path_mappings.erase (ii); auto ij = reverse_path_mappings.find (id); if (ij != reverse_path_mappings.end ()) reverse_path_mappings.erase (ij); auto ik = decls_within_rib.find (id); if (ik != decls_within_rib.end ()) decls_within_rib.erase (ik); } void Rib::append_reference_for_def (NodeId def, NodeId ref) { references[def].insert (ref); } bool Rib::have_references_for_node (NodeId def) const { auto it = references.find (def); if (it == references.end ()) return false; return !it->second.empty (); } bool Rib::decl_was_declared_here (NodeId def) const { for (auto &it : decls_within_rib) { if (it.first == def) return true; } return false; } bool Rib::lookup_decl_type (NodeId def, ItemType *type) const { auto it = decl_type_mappings.find (def); if (it == decl_type_mappings.end ()) return false; *type = it->second; return true; } void Rib::debug () const { fprintf (stderr, "%s\n", debug_str ().c_str ()); } std::string Rib::debug_str () const { std::string buffer; for (const auto &it : path_mappings) { buffer += it.first.get () + "=" + std::to_string (it.second); buffer += ","; } return "{" + buffer + "}"; } Scope::Scope (CrateNum crate_num) : crate_num (crate_num) {} void Scope::insert ( const CanonicalPath &ident, NodeId id, location_t locus, bool shadow, Rib::ItemType type, std::function dup_cb) { peek ()->insert_name (ident, id, locus, shadow, type, dup_cb); } void Scope::insert (const CanonicalPath &ident, NodeId id, location_t locus, Rib::ItemType type) { peek ()->insert_name (ident, id, locus, true, type, [] (const CanonicalPath &, NodeId, location_t) -> void { }); } bool Scope::lookup (const CanonicalPath &ident, NodeId *id) { NodeId lookup = UNKNOWN_NODEID; iterate ([&] (Rib *r) mutable -> bool { if (r->lookup_name (ident, &lookup)) return false; return true; }); *id = lookup; return lookup != UNKNOWN_NODEID; } bool Scope::lookup_decl_type (NodeId id, Rib::ItemType *type) { bool found = false; iterate ([&] (const Rib *r) -> bool { if (r->decl_was_declared_here (id)) { bool ok = r->lookup_decl_type (id, type); rust_assert (ok); found = true; return false; } return true; }); return found; } bool Scope::lookup_rib_for_decl (NodeId id, const Rib **rib) { bool found = false; iterate ([&] (const Rib *r) -> bool { if (r->decl_was_declared_here (id)) { *rib = r; found = true; return false; } return true; }); return found; } void Scope::iterate (std::function cb) { for (auto it = stack.rbegin (); it != stack.rend (); ++it) { if (!cb (*it)) return; } } void Scope::iterate (std::function cb) const { for (auto it = stack.rbegin (); it != stack.rend (); ++it) { if (!cb (*it)) return; } } Rib * Scope::peek () { return stack.back (); } void Scope::push (NodeId id) { stack.push_back (new Rib (get_crate_num (), id)); } Rib * Scope::pop () { Rib *r = peek (); stack.pop_back (); return r; } void Scope::append_reference_for_def (NodeId refId, NodeId defId) { bool ok = false; iterate ([&] (Rib *r) mutable -> bool { if (r->decl_was_declared_here (defId)) { ok = true; r->append_reference_for_def (defId, refId); } return true; }); rust_assert (ok); } bool Scope::decl_was_declared_here (NodeId def) const { bool found = false; iterate ([&] (const Rib *r) -> bool { if (r->decl_was_declared_here (def)) { found = true; return false; } return true; }); return found; } 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 ())), macro_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; } void Resolver::push_new_macro_rib (Rib *r) { rust_assert (label_ribs.find (r->get_node_id ()) == label_ribs.end ()); macro_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; } bool Resolver::find_macro_rib (NodeId id, Rib **rib) { auto it = macro_ribs.find (id); if (it == macro_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 (), BUILTINS_LOCATION, false, Rib::ItemType::Type, [] (const CanonicalPath &, NodeId, location_t) -> 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 ()); auto never = new TyTy::NeverType (mappings->get_next_hir_id ()); setup_builtin ("u8", u8); setup_builtin ("u16", u16); setup_builtin ("u32", u32); setup_builtin ("u64", u64); setup_builtin ("u128", u128); setup_builtin ("i8", i8); setup_builtin ("i16", i16); setup_builtin ("i32", i32); setup_builtin ("i64", i64); setup_builtin ("i128", i128); setup_builtin ("bool", rbool); setup_builtin ("f32", f32); setup_builtin ("f64", f64); setup_builtin ("usize", usize); setup_builtin ("isize", isize); setup_builtin ("char", char_tyty); setup_builtin ("str", str); setup_builtin ("!", never); // unit type () TyTy::TupleType *unit_tyty = TyTy::TupleType::get_unit_type (mappings->get_next_hir_id ()); std::vector > elems; AST::TupleType *unit_type = new AST::TupleType (std::move (elems), BUILTINS_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::setup_builtin (const std::string &name, TyTy::BaseType *tyty) { AST::PathIdentSegment seg (name, BUILTINS_LOCATION); auto typePath = ::std::unique_ptr ( new AST::TypePathSegment (::std::move (seg), false, BUILTINS_LOCATION)); ::std::vector< ::std::unique_ptr > segs; segs.push_back (::std::move (typePath)); auto builtin_type = new AST::TypePath (::std::move (segs), BUILTINS_LOCATION, false); builtins.push_back (builtin_type); tyctx->insert_builtin (tyty->get_ref (), builtin_type->get_node_id (), tyty); mappings->insert_node_to_hir (builtin_type->get_node_id (), tyty->get_ref ()); mappings->insert_canonical_path ( builtin_type->get_node_id (), CanonicalPath::new_seg (builtin_type->get_node_id (), name)); } void Resolver::insert_resolved_name (NodeId refId, NodeId defId) { resolved_names[refId] = defId; get_name_scope ().append_reference_for_def (refId, defId); insert_captured_item (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; } void Resolver::insert_resolved_macro (NodeId refId, NodeId defId) { auto it = resolved_macros.find (refId); rust_assert (it == resolved_macros.end ()); resolved_labels[refId] = defId; get_label_scope ().append_reference_for_def (refId, defId); } bool Resolver::lookup_resolved_macro (NodeId refId, NodeId *defId) { auto it = resolved_macros.find (refId); if (it == resolved_macros.end ()) return false; *defId = it->second; return true; } void Resolver::insert_resolved_misc (NodeId refId, NodeId defId) { auto it = misc_resolved_items.find (refId); rust_assert (it == misc_resolved_items.end ()); misc_resolved_items[refId] = defId; } bool Resolver::lookup_resolved_misc (NodeId refId, NodeId *defId) { auto it = misc_resolved_items.find (refId); if (it == misc_resolved_items.end ()) return false; *defId = it->second; return true; } void Resolver::push_closure_context (NodeId closure_expr_id) { auto it = closures_capture_mappings.find (closure_expr_id); rust_assert (it == closures_capture_mappings.end ()); closures_capture_mappings.insert ({closure_expr_id, {}}); closure_context.push_back (closure_expr_id); } void Resolver::pop_closure_context () { rust_assert (!closure_context.empty ()); closure_context.pop_back (); } void Resolver::insert_captured_item (NodeId id) { // nothing to do unless we are in a closure context if (closure_context.empty ()) return; // check that this is a VAR_DECL? Scope &name_scope = get_name_scope (); Rib::ItemType type = Rib::ItemType::Unknown; bool found = name_scope.lookup_decl_type (id, &type); if (!found) return; // RIB Function { let a, let b } id = 1; // RIB Closure { let c } id = 2; // RIB IfStmt { } id = 3; // RIB ... { ... } id = 4 // // if we have a resolved_node_id of 'a' and the current rib is '3' we know // this is binding exists in a rib with id < the closure rib id, other wise // its just a normal binding and we don't care // // Problem the node id's dont work like this because the inner most items are // created first so this means the root will have a larger id and a simple // less than or greater than check wont work for more complex scoping cases // but we can use our current rib context to figure this out by checking if // the rib id the decl we care about exists prior to the rib for the closure // id const Rib *r = nullptr; bool ok = name_scope.lookup_rib_for_decl (id, &r); rust_assert (ok); NodeId decl_rib_node_id = r->get_node_id (); // iterate the closure context and add in the mapping for all to handle the // case of nested closures for (auto &closure_expr_id : closure_context) { if (!decl_needs_capture (decl_rib_node_id, closure_expr_id, name_scope)) continue; // is this a valid binding to take bool is_var_decl_p = type == Rib::ItemType::Var; if (!is_var_decl_p) { // FIXME is this an error case? return; } // append it to the context info auto it = closures_capture_mappings.find (closure_expr_id); rust_assert (it != closures_capture_mappings.end ()); it->second.insert (id); } } bool Resolver::decl_needs_capture (NodeId decl_rib_node_id, NodeId closure_rib_node_id, const Scope &scope) { for (const auto &rib : scope.get_context ()) { bool rib_is_closure = rib->get_node_id () == closure_rib_node_id; bool rib_is_decl = rib->get_node_id () == decl_rib_node_id; if (rib_is_closure) return false; else if (rib_is_decl) return true; } return false; } const std::set & Resolver::get_captures (NodeId id) const { auto it = closures_capture_mappings.find (id); rust_assert (it != closures_capture_mappings.end ()); return it->second; } } // namespace Resolver } // namespace Rust