dotc vs scalac: Trees, Symbols, Types & Denotations - smarter/dotty GitHub Wiki

Overview explanation how symbols, named types and denotations hang together: Denotations.scala:22

Denotation

Comment with a few details: Denotations.scala:70

A Denotation is the result of a name lookup during a given period

  • Most properties of symbols are now in the denotation (name, type, owner, etc.)
  • Denotations usually have a reference to the selected symbol
  • Denotations may be overloaded (MultiDenotation). In this case the symbol may be NoSymbol (the two variants have symbols).
  • Non-overloaded denotations have an info

Denotations of methods have a signature (Signature.scala:7), which uniquely identifies overloaded methods.

Denotation vs. SymDenotation

A SymDenotation is an extended denotation that has symbol-specific properties (that may change over phases)

  • flags
  • annotations
  • info

SymDenotation implements lazy types (similar to scalac). The type completer assigns the denotation's info.

Implicit Conversion

There is an implicit conversion

core.Symbols.toDenot(sym: Symbol)(implicit ctx: Context): SymDenotation

Because the class Symbol is defined in the object core.Symbols, the implicit conversion does not need to be imported, it is part of the implicit scope of the type Symbol (check the Scala spec). However, it can only be applied if an implicit Context is in scope.

Symbol

  • Symbol instances have a SymDenotation
  • Most symbol properties in scalac are now in the denotation (in dotc)

Most of the isFooBar properties in scalac don't exist anymore in dotc. Use flag tests instead, for example

if (sym.isPackageClass)         // scalac
if (sym is Flags.PackageClass)  // dotc (*)

(*) Symbols are implicitly converted to their denotation, see above. Each SymDeotation has flags that can be queried using the is method.

Flags

  • Flags are instances of the value class FlagSet, which encapsulates a Long.
  • Each flag is either valid for types, terms, or both.
000..0001000..01
        ^     ^^
        flag  | \
              |  valid for term
              valid for type
```

* Example: `Module` is valid for both module values and module classes, `ModuleVal` / `ModuleClass` for either of the two.
* `flags.is(Method | Param)`: true if `flags` has either of the two
* `flags.is(allOf(Method | Deferred))`: true if `flags` has both. `allOf` creates a `FlagConjunction`, so a different overload of `is` is chosen.
  * Careful: `flags.is(Method & Deferred)` is always true, because `Method & Deferred` is empty.

### Tree

* Trees don't have symbols
  * `tree.symbol` is `tree.denot.symbol`
  * `tree.denot` is `tree.tpe.denot` where the `tpe` is a `NamdedType` (see next point)
* Subclasses of `DenotingTree` (`Template`, `ValDef`, `DefDef`, `Select`, `Ident`, etc.) have a `NamedType`, which has a `denot` field. The denotation has a symbol.
  * The `denot` of a `NamedType` (prefix + name) for the current period is obtained from the symbol that the type refers to. This symbol is searched using `prefix.member(name)`.


### Type

 * `MethodType(paramSyms, resultType)` from scalac => `mt @ MethodType(paramNames, paramTypes)`. Result type is `mt.resultType`

`@todo`