Edit this page on GitHub

Symbols

As discussed previously, dotc maintains time-indexed views of various compiler artifacts. The following sections discuss how they are managed in the compiler.

Symbols

Defined in Symbols, a Symbol is a unique identifier for a definition (e.g. a method, type, or field). A ClassSymbol extends Symbol and represents either a class, or a trait, or an object. A Symbol can even refer to non-Scala entities, such as from the Java standard library.

Definitions are Dynamic

Traditionally, compilers store context-dependent data in a symbol table. Where a symbol then is the central reference to address context-dependent data. dotc instead uses a phase-indexed function (known as a Denotation) to compute views of definitions across phases, as many of attributes associated with definitions are phase-dependent. For example:

  • types are gradually simplified by several phases,
  • owners change in lambdaLift (local methods are lifted to an enclosing class) and flatten (when inner classes are moved to the top level)
  • Names are changed when private members need to be accessed from outside their class (for instance from a nested class or a class implementing a trait).

Additionally, symbols are not suitable to be used as a reference to a definition in another compilation unit. In the context of incremental compilation, a symbol from an external compilation unit may be deleted or changed, making the reference stale. To counter this, dotc types trees of cross-module references with either a TermRef or TypeRef. A reference type contains a prefix type and a name. The denotation that the type refers to is established dynamically based on these fields.

Denotations

On its own a Symbol has no structure. Its semantic meaning is given by being associated with a Denotation.

A denotation is the result of resolving a name during a given period, containing the information describing some entity (either a term or type), indexed by phase. Denotations usually have a reference to a selected symbol, but not always, for example if the denotation is overloaded, i.e. a MultiDenotation.

SymDenotations

All definition symbols will contain a SymDenotation. The denotation, in turn, contains:

  • a reverse link to the source symbol
  • a reference to the enclosing symbol that defined the source symbol:
    • for a local variable, the enclosing method
    • for a field or class, the enclosing class
  • a set of flags, describing the definition (e.g. whether it's a trait or mutable).
  • the type of the definition (through the info method)
  • a signature, which uniquely identifies overloaded methods (or else NotAMethod).
  • and more.

A class symbol will instead be associated with a ClassDenotation, which extends SymDenotation with some additional fields specific for classes.