// 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
// .
#ifndef RUST_NAME_RESOLVER_H
#define RUST_NAME_RESOLVER_H
#include "rust-system.h"
#include "rust-canonical-path.h"
#include "rust-hir-map.h"
#include "rust-hir-type-check.h"
namespace Rust {
namespace Resolver {
class Rib
{
public:
// Rust uses local_def_ids assigned by def_collector on the AST
// lets use NodeId instead
Rib (CrateNum crateNum, NodeId node_id)
: crate_num (crateNum), node_id (node_id),
mappings (Analysis::Mappings::get ())
{}
~Rib () {}
// this takes the relative paths of items within a compilation unit for lookup
void insert_name (
const CanonicalPath &path, NodeId id, Location locus, bool shadow,
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 (std::pair (id, path));
decls_within_rib.insert (std::pair (id, locus));
references[id] = {};
}
bool 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;
}
bool lookup_canonical_path (const NodeId &id, CanonicalPath *ident)
{
auto it = reverse_path_mappings.find (id);
if (it == reverse_path_mappings.end ())
return false;
*ident = it->second;
return true;
}
void 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);
}
CrateNum get_crate_num () const { return crate_num; }
NodeId get_node_id () const { return node_id; }
void iterate_decls (std::function cb)
{
for (auto it : decls_within_rib)
{
if (!cb (it.first, it.second))
return;
}
}
void iterate_references_for_def (NodeId def, std::function cb)
{
auto it = references.find (def);
if (it == references.end ())
return;
for (auto ref : it->second)
{
if (!cb (ref))
return;
}
}
void append_reference_for_def (NodeId def, NodeId ref)
{
references[def].insert (ref);
}
bool have_references_for_node (NodeId def) const
{
auto it = references.find (def);
if (it == references.end ())
return false;
return !it->second.empty ();
}
bool decl_was_declared_here (NodeId def) const
{
for (auto &it : decls_within_rib)
{
if (it.first == def)
return true;
}
return false;
}
private:
CrateNum crate_num;
NodeId node_id;
std::map path_mappings;
std::map reverse_path_mappings;
std::map decls_within_rib;
std::map> references;
Analysis::Mappings *mappings;
};
class Scope
{
public:
Scope (CrateNum crate_num) : crate_num (crate_num) {}
~Scope () {}
void
insert (const CanonicalPath &ident, NodeId id, Location locus, bool shadow,
std::function dup_cb)
{
peek ()->insert_name (ident, id, locus, shadow, dup_cb);
}
void insert (const CanonicalPath &ident, NodeId id, Location locus)
{
peek ()->insert_name (ident, id, locus, true,
[] (const CanonicalPath &, NodeId, Location) -> void {
});
}
bool 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;
}
void iterate (std::function cb)
{
for (auto it = stack.rbegin (); it != stack.rend (); ++it)
{
if (!cb (*it))
return;
}
}
Rib *peek () { return stack.back (); }
void push (NodeId id) { stack.push_back (new Rib (get_crate_num (), id)); }
Rib *pop ()
{
Rib *r = peek ();
stack.pop_back ();
return r;
}
CrateNum get_crate_num () const { return crate_num; }
void 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);
}
private:
CrateNum crate_num;
std::vector stack;
};
// This can map simple NodeIds for names to their parent node
// for example:
//
// var x = y + 1;
//
// say y has node id=1 and the plus_expression has id=2
// then the Definition will have
// Definition { node=1, parent=2 }
// this will be used later to gather the ribs for the type inferences context
//
// if parent is UNKNOWN_NODEID then this is a root declaration
// say the var_decl hasa node_id=4;
// the parent could be a BLOCK_Expr node_id but lets make it UNKNOWN_NODE_ID
// so we know when it terminates
struct Definition
{
NodeId node;
NodeId parent;
// add kind ?
bool is_equal (const Definition &other)
{
return node == other.node && parent == other.parent;
}
};
class Resolver
{
public:
static Resolver *get ();
~Resolver () {}
// these builtin types
void insert_builtin_types (Rib *r);
// these will be required for type resolution passes to
// map back to tyty nodes
std::vector &get_builtin_types ();
void push_new_name_rib (Rib *r);
void push_new_type_rib (Rib *r);
void push_new_label_rib (Rib *r);
bool find_name_rib (NodeId id, Rib **rib);
bool find_type_rib (NodeId id, Rib **rib);
bool find_label_rib (NodeId id, Rib **rib);
void insert_new_definition (NodeId id, Definition def);
bool lookup_definition (NodeId id, Definition *def);
void insert_resolved_name (NodeId refId, NodeId defId);
bool lookup_resolved_name (NodeId refId, NodeId *defId);
void insert_resolved_type (NodeId refId, NodeId defId);
bool lookup_resolved_type (NodeId refId, NodeId *defId);
void insert_resolved_label (NodeId refId, NodeId defId);
bool lookup_resolved_label (NodeId refId, NodeId *defId);
// proxy for scoping
Scope &get_name_scope () { return name_scope; }
Scope &get_type_scope () { return type_scope; }
Scope &get_label_scope () { return label_scope; }
NodeId get_global_type_node_id () { return global_type_node_id; }
void set_unit_type_node_id (NodeId id) { unit_ty_node_id = id; }
NodeId get_unit_type_node_id () { return unit_ty_node_id; }
void mark_decl_mutability (NodeId id, bool mut)
{
rust_assert (decl_mutability.find (id) == decl_mutability.end ());
decl_mutability[id] = mut;
}
bool decl_is_mutable (NodeId id) const
{
auto it = decl_mutability.find (id);
rust_assert (it != decl_mutability.end ());
return it->second;
}
void mark_assignment_to_decl (NodeId id, NodeId assignment)
{
auto it = assignment_to_decl.find (id);
if (it == assignment_to_decl.end ())
assignment_to_decl[id] = {};
assignment_to_decl[id].insert (assignment);
}
size_t get_num_assignments_to_decl (NodeId id) const
{
auto it = assignment_to_decl.find (id);
if (it == assignment_to_decl.end ())
return 0;
return it->second.size ();
}
void iterate_name_ribs (std::function cb)
{
for (auto it = name_ribs.begin (); it != name_ribs.end (); it++)
if (!cb (it->second))
break;
}
void iterate_type_ribs (std::function cb)
{
for (auto it = type_ribs.begin (); it != type_ribs.end (); it++)
{
if (it->first == global_type_node_id)
continue;
if (!cb (it->second))
break;
}
}
void iterate_label_ribs (std::function cb)
{
for (auto it = label_ribs.begin (); it != label_ribs.end (); it++)
if (!cb (it->second))
break;
}
private:
Resolver ();
void generate_builtins ();
Analysis::Mappings *mappings;
TypeCheckContext *tyctx;
std::vector builtins;
Scope name_scope;
Scope type_scope;
Scope label_scope;
NodeId global_type_node_id;
NodeId unit_ty_node_id;
// map a AST Node to a Rib
std::map name_ribs;
std::map type_ribs;
std::map label_ribs;
// map any Node to its Definition
// ie any name or type usage
std::map name_definitions;
// Rust uses DefIds to namespace these under a crate_num
// but then it uses the def_collector to assign local_defids
// to each ast node as well. not sure if this is going to fit
// with gcc very well to compile a full crate in one go but we will
// see.
// these are of the form ref->Def-NodeId
// we need two namespaces one for names and ones for types
std::map resolved_names;
std::map resolved_types;
std::map resolved_labels;
// map of resolved names mutability flag
std::map decl_mutability;
// map of resolved names and set of assignments to the decl
std::map> assignment_to_decl;
};
} // namespace Resolver
} // namespace Rust
#endif // RUST_NAME_RESOLVER_H