Name Resolution Cheatsheet
The Name Resolution stage is not that difficult stage of compilation, anyway tricky one. Here I tried to collect workflows for different resolution cases and connections between them.
Okay, what classes do we have:
ModuleTreeBuilder- by the name you can see that this one builds a module tree.Importer- this one resolvesusedeclarations and imports new items to the module tree.NameResolver- resolves names, that is, binds each usage to the definition.PathResolver- helper class that unifies path resolution logic for all name resolution sub-stages.DefTable- common definitions info storage
Basic structures
Symbol
Identifies interned string, read more about symbols here
Namespace
An enumeration of namespaces. Each namespace is a separate storage for definitions, thus type names do not collide with value names, etc.
There is one special variant - Namespace::Any, it is not used in definition storages and mappings, by the way, is used in methods, e.g. to collect definitions with the same name from all namespaces. Never store Namespace::Any in DefTable, only use it as a helper.
Def
Definition structure, holds DefKind and DefId. You can access particular definition via DefTable::getDef or get all, defined, using DefTable::getDefinitions
DefId & DefIndex
DefIndex is a simple index type, i.e. integer wrapper to create a distinct integer type (C++ does not support it).
DefId is a unique definition identifier, currently, it only holds DefIndex but might be extended in the future.
DefId::index is the index of vector from DefTable::defs. You can get particular definition by DefId or DefIndex from DefTable via getDef.
ROOT_DEF_ID
The constant for root module definition, used in many places for validations and logic checks.
DefKind
Enumeration of definitions kinds. Each kind has some properties, e.g. namespace where it will be defined.
You can get DefKind from Def structure if you have one on hand.
To find out in which namespace specific DefKind must be defined call Def::getDefKindNS.
Vis
Visibility enumeration, for now only Vis::Unset and Vis::Pub exist.
PerNS<T>
A helper template structure that stores a value of some type for each namespace.
PrimTypeSet
Bits-optimized collection of primitive types flags. It can be used to mark specific primitive types used/shadowed, etc. Used by the module to save shadowed primitive types.
Module
The Module is a single node of the Module Tree.
Fields:
- kind - Kind of module, i.e.
Def(named module bound to some definition) orBlock(anonymous module). Type is theModuleKindenumeration. - id - Identifier of module - either
DefId(forModuleKind::Def) orNodeId(forModuleKind::Block). - parent - Optional parent that is another module (only root module does not have a parent).
- nearestModDef - Nearest definition of a kind
DefKind::Mod, always present. For modules which areDefKind::Modby themselves nearestModDef point to the same modules (root module is also of a kindDefKind, thus nearestModDef of root module is the same asROOT_DEF_ID). - perNS - PerNS<map<Symbol, [NameBinding](#namebinding)>>. A per-namespace collection of mappings Symbol (some name) -> DefId (some definition).
- shadowedPrimTypes - module
PrimTypeSet, i.e. flags showing which primitive types (e.g.int,f32) are shadowed in the module.
NameBinding
The Module binds names either to FOS or to some definition.
This is why NameBinding exists, it is an ADT for FOS and DefId.
FOSId
FOS stands for "Function Overload Set". In Jacy you can overload functions via different label names, i.e. not by types but func foo(from: int) and func foo(to: int) can exist together.
FOSId is a unique identifier for one FOS -- a collection of functions with the same name, defined in the same module.
For example, in:
mod m {
func foo(from: int) {}
func foo(to: int) {}
}
mod m only holds NameBinding with name foo (base name of FOS) which points to FOSId of FOS foo in DefTable.
To access a specific function, at first, you need to get FOSId from the module and then go to the DefTable to search for a suffix.
Function suffix is an interned string such as (from:) or (to:), i.e. function label list.
DefTable
DefTable holds all data, we need, about definitions and relations between them.
Fields, Storages
Direct storage mark means that this field is the final storage, i.e. it is not a mapping and other items map to it, Indirect storage is an opposite.
- defs -
vector<Def>- Direct storage - Definition collection,DefIndexpoints to index in defs. - modules -
DefMap<Module::Ptr>- Direct storage - Maps definitions to modules -- Named modules. Used everywhere, filled in theModuleTreeBuilder. - blocks -
NodeMap<Module::Ptr>- Direct storage - Maps block nodes to modules -- Anonymous modules. Used everywhere, filled in theModuleTreeBuilder. - useDeclModules -
NodeMap<Module::Ptr>- Indirect storage - Maps node id ofuseitem to module it defined it. Used byImporter, filled in theModuleTreeBuilder. - defVisMap -
DefMap<Vis>- Maps definition to its visibility. - nodeIdDefIdMap - - Maps definition node id to its
DefId. - defIdNodeIdMap - Maps
DefIdto definition node id. - importAliases - Maps
DefIdof import alias, i.e. definition appeared fromusedeclaration to another definition (that also might be an import alias).
Basic API
This API is almost a list of helpers to retrieve items from the fields described above.
Working with definitions:
- getDef(DefId/DefIndex) -> Def - get definition by
DefIdorDefIndex. - getDefUnwind(DefId) -> Def - get definition unwinding aliases (if definition is an
ImportAlias). - getDefVis(DefId) -> Vis - get definition visibility.
- getNodeIdByDefId(DefId) -> NodeId - get node id of definition node by definition id.
- getDefIdByNodeId(NodeId) -> DefId - get definition id by node id.
- getDefNameSpan(DefId) -> Span - get span of definition identifier (e.g. in
func foo() {}it returns span forfoo).
- getDef(DefId/DefIndex) -> Def - get definition by
Working with modules:
- getModule(DefId) -> Module::Ptr - get module by definition id.
- getBlock(NodeId) -> Module::Ptr - get block (anonymous module) by node id.
- getFuncModule(FOSId, Symbol) -> Module::Ptr - get function module by
FOSIdand specific suffix, together they non-ambiguously specify some function. - addModule(DefId, Module::Ptr) - Add named module, binding it by
DefId. - addBlock(NodeId, Module::Ptr) - Add block (anonymous module), binding it by NodeId.