aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/transform/Erasure.scala
blob: 03726e4f5566ad43ec423e88d3eb47e2d1460f6f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package dotty.tools.dotc
package core
package transform

import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._

object Erasure {

  /**  The erasure |T| of a type T. This is:
   *
   *   - For a refined type scala.Array+[T]:
   *      - if T is Nothing or Null, scala.Array+[Object]
   *      - otherwise, if T <: Object, scala.Array+[|T|]
   *      - otherwise, if T is a type paramter coming from Java, scala.Array+[Object].
   *      - otherwise, Object
   *   - For a constant type, NoType or NoPrefix, the type itself.
   *   - For all other type proxies: The erasure of the underlying type.
   *   - For a typeref scala.Any, scala.AnyVal, scala.Singleon or scala.NotNull, java.lang.Object.
   *   - For a typeref scala.Unit, scala.runtime.BoxedUnit.
   *   - For a typeref P.C where C refers to a toplevel class, P.C.
   *   - For a typeref P.C where C refers to a nested class, |P|.C.
   *   - For a typeref P.C where C refers to an alias type, the erasure of C's alias.
   *   - For a typeref P.C where C refers to an abstract type, the erasure of C's upper bound.
   *   - For T1 & T2, erasure(T1)  (??)
   *   - For T1 | T2, the first base class in the linearization of T which is also a base class of T2
   *   - For a method type (Fs)scala.Unit, (|Fs|)scala.Unit.
   *   - For any other method type (Fs)T, (|Fs|)|T|.
   *   - For a polymorphic type, the erasure of its result type.
   *   - For the class info type of java.lang.Object, the same type without any parents.
   *   - For a class info type of a value class, the same type without any parents.
   *   - For any other class info type with parents Ps, the same type with
   *     parents |Ps|, but with duplicate references of Object removed.
   *   - For any other type, exception.
   */
  def erasure(tp: Type)(implicit ctx: Context): Type = tp match {
    case tp: TypeRef =>
      val sym = tp.symbol
      if (sym.isClass)
        /*if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tref)
        else */if (sym.owner is Package) normalizeClass(sym.asClass).typeRef
        else tp.derivedSelect(erasure(tp.prefix))
      else erasure(tp.info)
    case tp: RefinedType =>
      val parent = tp.parent
      if (parent isRef defn.ArrayClass) eraseArray(tp)
      else erasure(parent)
    case ConstantType(_) | NoType | NoPrefix =>
      tp
    case tp: TypeProxy =>
      erasure(tp.underlying)
    case AndType(tp1, tp2) =>
      erasure(tp1)
    case OrType(tp1, tp2) =>
      erasure(tp.baseType(lubClass(tp1, tp2)))
    case tp: MethodType =>
      tp.derivedMethodType(
        tp.paramNames, tp.paramTypes.mapConserve(erasure), resultErasure(tp.resultType))
    case tp: PolyType =>
      erasure(tp.resultType)
    case tp @ ClassInfo(pre, cls, classParents, decls, _) =>
      val parents: List[TypeRef] =
        if (cls == defn.ObjectClass || cls.isPrimitiveValueClass) Nil
        else if (cls == defn.ArrayClass) defn.ObjectClass.typeRef :: Nil
        else removeLaterObjects(classParents mapConserve (erasure(_).asInstanceOf[TypeRef]))
      tp.derivedClassInfo(erasure(pre), parents, NoType)
    case ErrorType =>
      tp
  }

  def eraseArray(tp: RefinedType)(implicit ctx: Context) = {
    val (n, elemtp) = tp.splitArray
    val elemCls = elemtp.classSymbol
    if (elemCls.isSubClass(defn.NullClass))
      defn.ObjectArrayType
    else if (elemCls.isSubClass(defn.ObjectClass) || elemCls.isPrimitiveValueClass)
      (erasure(elemtp) /: (0 until n))((erased, _) =>
        defn.ArrayType.appliedTo(erased))
    else if (elemtp.typeSymbol is JavaDefined)
      defn.ObjectArrayType
    else
      defn.ObjectType
  }

  def normalizeClass(cls: ClassSymbol)(implicit ctx: Context): ClassSymbol = {
    if (cls.owner == defn.ScalaPackageClass) {
      if (cls == defn.AnyClass || cls == defn.AnyValClass || cls == defn.SingletonClass || cls == defn.NotNullClass)
        return defn.ObjectClass
      if (cls == defn.UnitClass)
        return defn.BoxedUnitClass
    }
    cls
  }

  def lubClass(tp1: Type, tp2: Type)(implicit ctx: Context): ClassSymbol = {
    var bcs1 = tp1.baseClasses
    val bc2 = tp2.baseClasses.head
    while (bcs1.nonEmpty && !bc2.derivesFrom(bcs1.head))
      bcs1 = bcs1.tail
    if (bcs1.isEmpty) defn.ObjectClass else bcs1.head
  }

  /** The parameter signature of a type.
   *  Need to ensure correspondence with erasure
   */
  def paramSignature(tp: Type)(implicit ctx: Context): TypeName = tp match {
    case tp: TypeRef =>
      val sym = tp.symbol
      if (sym.isClass)
        /*if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tref)
        else */if (sym.owner is Package) normalizeClass(sym.asClass).name
        else sym.asClass.name
      else paramSignature(tp.info)
    case tp: RefinedType =>
      val parent = tp.parent
      if (parent isRef defn.ArrayClass)
        eraseArray(tp) match {
          case tp1: RefinedType if tp1.parent isRef defn.ArrayClass =>
            paramSignature(tp1.refinedInfo) ++ "[]"
          case tp1 =>
            paramSignature(tp1)
        }
      else paramSignature(parent)
    case tp: TypeProxy =>
      paramSignature(tp.underlying)
    case AndType(tp1, tp2) =>
      paramSignature(tp1)
    case OrType(tp1, tp2) =>
      lubClass(tp1, tp2).name
    case ErrorType =>
      tpnme.WILDCARD
  }

  def resultErasure(tp: Type)(implicit ctx: Context) =
    if (tp isRef defn.UnitClass) tp else erasure(tp)

  def removeLaterObjects(trs: List[TypeRef])(implicit ctx: Context): List[TypeRef] = trs match {
    case tr :: trs1 => tr :: trs1.filterNot(_ isRef defn.ObjectClass)
    case nil => nil
  }
}