Most things in the compiler has a name. Therefore, it is good to know how name resolution works in the compiler. Storm provides ways to alter the way name lookups are performed on a case-by-case basis. Therefore, this section mostly describes the framework as well as the expected way to look up names.
A name is a sequence of parts, much like a path in your file system. In a file system each part only contains a name, but in Storm, each part also contains zero or more type parameters. These type parameters are direct references to a type in the type system, so no arbitrary strings or values are allowed as parameters to parts. This does not make much sense if we only consider packages (which usually does not take any parameters), but if we also consider template types and functions, it starts making sense. In this way, a name is either an absolute or relative path to something in the type system.
Everything in the type system inherits from the class
Named contains, just
like each part of a name, a string and zero or more type parameters.
Named also inherits from
NameLookup, which is an interface indicating that it is possible to look up names relative to that
point in the type system. This means, that given a name and an entry point, we can traverse the
Name hierarchy to find out what the name resolves to.
This process works as long as all names are relative to some fixed point. However, this often
becomes cumbersome since we either have to specify full names of everything we are using, or we have
to restrict ourselves to only a part of the type system (there is nothing that is equivalent to
in names). Therefore, Storm has something that is called a Scope.
Scope is an entry point into the type hierarchy along with a policy of how we want to traverse
the type system. This policy might be: traverse from the entry point, if that fails, assume the name
is an absolute name and traverse from the root of the type system. This can also be extended to look
at any includes as well, or to do any number of interesting things. Using this mechanism, it is
possible for one language to "leak" its name resolution semantics into other languages. This may be
good or confusing, depending on how it is done. Consider, for example, embedding languages into each
other. In this case it makes sense in some cases that the embedded language follows the name
resolution of the top-level language. In other cases it does not.
As mentioned earlier, the parameters present at each part of a name are used to implement function
overloading and templates. This functionality is implemented by the class
NameSet, which is
basically a collection of different names. The
NameSet enforces that names does not collide with
each other (considering parameter), and implements how to resolve parameters. By default,
considers inheritance when examining which overloads are suitable. This can, however, be disabled on
a overload-by-overload basis if desired.
NameSet also implements support for templates. A
Template is an object that can generate
Named objects on demand. Whenever the
requested for a name with parameters it does not find a match for, it asks a
Template with the
same name (if it is present) to generate the match.
Member functions and variables always have an explicit this pointer as their first parameter.
Named object is optionally associated with a visibility. The visibility determines which
parts of the system is allowed to access a particular
Named. This mechanism is used to implement
access controls, such as
private, in Storm, but allows more flexibility if desired.
The visibility of a
Named object is represented by instances of subclasses to the
class. This class is not much more than a predicate function,
visible(Named check, NameLookup source)
that is called by
Named.visibleFrom whenever Storm needs to determine if the object
check is visible
source. There are a couple of default implementations for common keywords provided by default, for
The visibility is assessed during type lookup. Thus, it is necessary to provide a
describes the caller's scope when calling
NameLookup.find(). It is also possible to override the
visibility system by providing an empty scope (i.e.
Scope()) to the
find function. If a
object is not associated with a
Visibility object, Storm considers it to be visible from everywhere.