Scala2Erasure

Erasure logic specific to Scala 2 symbols.

class Object
trait Matchable
class Any

Type members

Types

The equivalent of a Scala 2 type symbol.

The equivalent of a Scala 2 type symbol.

In some situations, nsc will create a symbol for a type where we wouldn't:

  • A with B with C { ... } is represented with a RefinedType whose symbol is a fresh class symbol whose parents are A, B, C.
  • Structural members also get their own symbols.

To emulate this, we simply use the type itself as a stand-in for its symbol.

See also sameSymbol which determines if two pseudo-symbols are really the same.

A type that would be represented as a RefinedType in Scala 2.

A type that would be represented as a RefinedType in Scala 2.

The RefinedType of Scala 2 contains both a list of parents and a list of refinements, intersections are represented as a RefinedType with no refinements.

A TypeRef that is known to represent a member of a structural type.

A TypeRef that is known to represent a member of a structural type.

Value members

Concrete methods

def checkSupported(tp: Type)(using Context): Unit

Is this a supported Scala 2 refinement or parent of such a type?

Is this a supported Scala 2 refinement or parent of such a type?

We do not allow types that look like: ((A with B) @foo) with C or: (A { type X <: ... })#X with C`

as it would make our implementation of Scala 2 intersection erasure significantly more complicated. The problem is that each textual appearance of an intersection or refinement in a parent corresponds to a fresh instance of RefinedType (because Scala 2 does not hash-cons these types) with a fresh synthetic class symbol, thus affecting the result of isNonBottomSubClass. To complicate the matter, the Scala 2 UnCurry phase will also recursively dealias parent types, thus creating distinct class symbols even in situations where the same type alias is used to refer to a given refinement. Note that types like (A with B) with C do not run into these issues because they get flattened into a single RefinedType with three parents, cf flattenedParents.

See sbt-test/scala2-compat/erasure/changes/Main.scala for examples.

Throws:
TypeError

if this type is unsupported.

A flattened list of parents of this intersection.

A flattened list of parents of this intersection.

Mimic what Scala 2 does: intersections like A with (B with C) are flattened to three parents.

def intersectionDominator(parents: List[Type])(using Context): Type

An emulation of Erasure#intersectionDominator from Scala 2.

An emulation of Erasure#intersectionDominator from Scala 2.

Accurately reproducing the behavior of this method is extremely difficult because it operates on the symbols of the non-erased parent types, an implementation detail of the compiler. Furthermore, these non-class symbols are passed to methods such as isNonBottomSubClass whose behavior is only specified for class symbols. Therefore, the accuracy of this method cannot be guaranteed, the best we can do is make sure it works on as many test cases as possible which can be run from sbt using:

sbt-test/scripted scala2-compat/erasure

The body of this method is made to look as much as the Scala 2 version as possible to make them easier to compare, cf: https://github.com/scala/scala/blob/v2.13.5/src/reflect/scala/reflect/internal/transform/Erasure.scala#L356-L389

The pseudo symbol of tp, see PseudoSymbol.

The pseudo symbol of tp, see PseudoSymbol.

The pseudo-symbol representation of a given type is chosen such that isNonBottomSubClass behaves like it would in Scala 2, in particular this lets us strip all aliases.

Extensions

Extensions

extension (psym: PseudoSymbol)
def isClass(using Context): Boolean

Is this a class symbol? Also returns true for refinements since they get a class symbol in Scala 2.

Is this a class symbol? Also returns true for refinements since they get a class symbol in Scala 2.

An emulation of Symbol#isNonBottomSubClass from Scala 2.

An emulation of Symbol#isNonBottomSubClass from Scala 2.

The documentation of the original method is:

Is this class symbol a subclass of that symbol, and is this class symbol also different from Null or Nothing?

Which sounds fine, except that it is also used with non-class symbols, so what does it do then? Its implementation delegates to Type#baseTypeSeq whose documentation states:

The base type sequence of T is the smallest set of [...] class types Ti, so that [...]

But this is also wrong: the sequence returned by baseTypeSeq can contain non-class symbols.

Given that we cannot rely on the documentation and that the implementation is extremely complex, this reimplementation is mostly based on reverse-engineering rules derived from the observed behavior of the original method.

def isTrait(using Context): Boolean

Is this a trait symbol?

Is this a trait symbol?

Would these two pseudo-symbols be represented with the same symbol in Scala 2?

Would these two pseudo-symbols be represented with the same symbol in Scala 2?