aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/core/transform/Erasure.scala
blob: 31679f05e8fdfcac2c4a0b2a302f41d1672dfba8 (plain) (tree)
1
2
3
4
5
6
7
8
9



                        
                                                                   


                
                                            
    




                                                                                      

                                                                        














                                                                                                     
                                       

                                                                 

                         

                                                                      
                                                                                    
                                                    


                            
                                        

                                               





                             
                                              










                                                                                            

                     

   

                                                            

                                           
                          
                                                                                   




















                                                                                                                    
                                                        











                                                                            
                                                                         



                                   
                         
                              
                                                        
                                                   


                               






                                   

                     

   
                                                      
                                                           

                                                                                                
                                                                            


                   
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).typeConstructor
        else tp.derivedNamedType(erasure(tp.prefix))
      else erasure(sym.info)
    case tp: RefinedType =>
      val parent = tp.parent
      if (parent.isArray) 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, optSelfType) =>
      val parents: List[TypeRef] =
        if (cls == defn.ObjectClass || cls.isPrimitiveValueClass) Nil
        else if (cls == defn.ArrayClass) defn.ObjectClass.typeConstructor :: 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(sym.info)
    case tp: RefinedType =>
      val parent = tp.parent
      if (parent.isArray)
        eraseArray(tp) match {
          case tp1: RefinedType if tp1.parent.isArray =>
            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.isClassType(defn.UnitClass)) tp else erasure(tp)

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