Names
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 core.lang.Named
. A 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.
A 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, NameSet
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 NameSet
is
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.
Visibility
Each 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 public
and private
, in Storm, but allows more flexibility if desired.
The visibility of a Named
object is represented by instances of subclasses to the Visibility
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
from source
. There are a couple of default implementations for common keywords provided by default, for
example Public
, TypePrivate
, TypeProtected
, PackagePrivate
, etc.
The visibility is assessed during type lookup. Thus, it is necessary to provide a Scope
that
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 Named
object is not associated with a Visibility
object, Storm considers it to be visible from everywhere.