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 resolvesuse
declarations 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 theModuleKind
enumeration. - 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::Mod
by 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,DefIndex
points 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 ofuse
item 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
DefId
to definition node id. - importAliases - Maps
DefId
of import alias, i.e. definition appeared fromuse
declaration 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
DefId
orDefIndex
. - 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
FOSId
and 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.