aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/TypeErasure.scala
blob: c8c54ed0340b364910902006e44ea77e22658048 (plain) (tree)
1
2
3
4
5
6

                        
 

                                                                                      
 



















                                                                  
 



                                                                                           





                                                                                                         
                                                   






                                                                     
                                                              









                                                                                
                                                                                                                     








                                                                                                                   





                                                                                               












                                                                                                   

                                         


                                                                                     






                                                              













                                                                                       





















                                                                                                 
 
                    






                                                                                               
                                                                                                                   
 
                                            
    




                                                                                      




                                                                                                   

                                                                                              

                                                                        
                                                                                                     
                      
                                                            

                                                                                       
                                                  
                                                                         



                                                                                        
                                                 
                                       
     
                                                               

                         
                       
                                                                               

                                                                     

                            
                                                      
                       
                       

                                         

                                    
                         
                         
                             
                                             
                            
                                                                  
                          
                                                                                   






                                                                                                       
                        



                                           
                                                            






                                                                          

                                                                                           
       


                                          
        

   










                                                                                     

                                                                    

   












                                                                                      








                                                                                                                    




                                                                                                        

                                                         
     
                                                                     

                         






                                                                              

                                         
                         
                            
                                    
                    



                            
                              
                     

   
package dotty.tools.dotc
package core

import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Flags.JavaDefined
import util.DotClass

/** Erased types are:
 *
 *  TypeRef(NoPrefix, denot is ClassDenotation)
 *  TermRef(NoPrefix, denot is SymDenotation)
 *  ThisType
 *  SuperType
 *  PolyParam, only for array types and isInstanceOf, asInstanceOf
 *  RefinedType, parent* is Array class
 *  TypeBounds, only for array types
 *  AnnotatedType
 *  MethodType ----+-- JavaMethodType
 *  PolyType, only for array types and isInstanceOf, asInstanceOf
 *  RefinedThis
 *  ClassInfo
 *  NoType
 *  NoPrefix
 *  WildcardType
 *  ErrorType
 */
object TypeErasure {

  case class ErasedValueType(cls: ClassSymbol, underlying: Type) extends CachedGroundType {
    override def computeHash = doHash(cls, underlying)
  }

  private def erasureIdx(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean) =
    (if (isJava) 1 else 0) +
    (if (isSemi) 2 else 0) +
    (if (isConstructor) 4 else 0) +
    (if (wildcardOK) 8 else 0)

  private val erasures = new Array[TypeErasure](16)

  for {
    isJava <- List(false, true)
    isSemi <- List(false, true)
    isConstructor <- List(false, true)
    wildcardOK <- List(false, true)
  } erasures(erasureIdx(isJava, isSemi, isConstructor, wildcardOK)) =
    new TypeErasure(isJava, isSemi, isConstructor, wildcardOK)

  /** Produces an erasure function.
   *  @param isJava        Arguments should be treated the way Java does it
   *  @param isSemi        Value classes are mapped in an intermediate step to
   *                       ErasedValueClass types, instead of going directly to
   *                       the erasure of the underlying type.
   *  @param isConstructor Argument forms part of the type of a constructor
   *  @param wildcardOK    Wildcards are acceptable (true when using the erasure
   *                       for computing a signature name).
   */
  private def erasureFn(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean): TypeErasure =
    erasures(erasureIdx(isJava, isSemi, isConstructor, wildcardOK))

  private val scalaErasureFn = erasureFn(isJava = false, isSemi = false, isConstructor = false, wildcardOK = false)
  private val scalaSigFn = erasureFn(isJava = false, isSemi = false, isConstructor = false, wildcardOK = true)
  private val javaSigFn = erasureFn(isJava = true, isSemi = false, isConstructor = false, wildcardOK = true)
  private val semiErasureFn = erasureFn(isJava = false, isSemi = true, isConstructor = false, wildcardOK = false)

  def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp)
  def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp)
  def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = {
    val normTp =
      if (tp.isRepeatedParam) tp.translateParameterized(defn.RepeatedParamClass, defn.SeqClass)
      else tp
    (if (isJava) javaSigFn else scalaSigFn).sigName(normTp)
  }

  /**  The symbol's erased info. This is the type's erasure, except for the following symbols:
   *
   *   - For $asInstanceOf           : [T]T
   *   - For $isInstanceOf           : [T]scala#Boolean
   *   - For Array[T].<init>         : [T]{scala#Int)Array[T]
   *   - For type members of Array   : The original info
   *   - For all other abstract types: = ?
   *   - For all other symbols       : the semi-erasure of their types, with
   *                                   isJava, isConstructor set according to symbol.
   */
  def transformInfo(sym: Symbol, tp: Type)(implicit ctx: Context): Type = {
    val erase = erasureFn(sym is JavaDefined, isSemi = true, sym.isConstructor, wildcardOK = false)
    if ((sym eq defn.Any_asInstanceOf) ||
        (sym eq defn.Any_isInstanceOf) ||
        (sym.owner eq defn.ArrayClass) && (sym.isType || sym.isConstructor)) sym.info
    else if (sym.isAbstractType) TypeAlias(WildcardType)
    else erase(tp)
  }

  def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !(
    (tp derivesFrom defn.ObjectClass) ||
    tp.classSymbol.isPrimitiveValueClass ||
    (tp.typeSymbol is JavaDefined))

  def erasedLub(tp1: Type, tp2: Type)(implicit ctx: Context): Type = {
    val cls2 = tp2.classSymbol
    def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match {
      case bc :: bcs1 =>
        if (cls2.derivesFrom(bc))
          if (!bc.is(Trait)) bc
          else loop(bcs1, if (bestSoFar.derivesFrom(bc)) bestSoFar else bc)
        else
          loop(bcs1, bestSoFar)
      case nil =>
        bestSoFar
    }
    loop(tp1.baseClasses, defn.ObjectClass).typeRef
  }

  def erasedGlb(tp1: Type, tp2: Type, isJava: Boolean)(implicit ctx: Context): Type = tp1 match {
    case defn.ArrayType(elem1) =>
      tp2 match {
        case defn.ArrayType(elem2) => defn.ArrayType(erasedGlb(elem1, elem2, isJava))
        case _ => defn.ObjectType
      }
    case _ =>
      tp2 match {
        case defn.ArrayType(_) => defn.ObjectType
        case _ =>
          val tsym1 = tp1.typeSymbol
          val tsym2 = tp2.typeSymbol
          if (!tsym2.exists) tp1
          else if (!tsym1.exists) tp2
          else if (!isJava && tsym1.derivesFrom(tsym2)) tp1
          else if (!isJava && tsym2.derivesFrom(tsym1)) tp2
          else if (tp1.typeSymbol.isRealClass) tp1
          else if (tp2.typeSymbol.isRealClass) tp2
          else tp1
      }
  }
}
import TypeErasure._

/**
 *  This is used as the Scala erasure during the erasure phase itself
   *  It differs from normal erasure in that value classes are erased to ErasedValueTypes which
   *  are then later converted to the underlying parameter type in phase posterasure.
 *
 */
class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean) extends DotClass {

  /**  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 term ref p.x, the type <noprefix> # x.
   *   - 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 whose symbol is owned by Array: The typeref itself, with prefix = <noprefix>
   *   - For a typeref P.C where C refers to a class, <noprefix> # 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 all other type proxies: The erasure of the underlying type.
   *   - For T1 & T2, the erased glb of |T1| and |T2| (see erasedGlb)
   *   - For T1 | T2, the first base class in the linearization of T which is also a base class of T2
   *   - For => T, ()T
   *   - For a method type (Fs)scala.Unit, (|Fs|)scala.Unit.
   *   - For any other uncurried method type (Fs)T, (|Fs|)|T|.
   *   - For a curried method type (Fs1)(Fs2)T, (|Fs1|,Es2)ET where (Es2)ET = |(Fs2)T|.
   *   - For a polymorphic type [Ts](Ps)T, |(Ps)T|
   *   _ For a polymorphic type [Ts]T where T is not a method type, ()|T|
   *   - 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 NoType or NoPrefix, the type itself.
   *   - For any other type, exception.
   */
  def apply(tp: Type)(implicit ctx: Context): Type = tp match {
    case tp: TypeRef =>
      val sym = tp.symbol
      if (!sym.isClass)
        if (sym.exists && (sym.owner eq defn.ArrayClass)) tp else this(tp.info)
      else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tp)
      else eraseNormalClassRef(tp)
    case tp: RefinedType =>
      val parent = tp.parent
      if (parent isRef defn.ArrayClass) eraseArray(tp)
      else this(parent)
    case tp: TermRef =>
      assert(tp.symbol.exists, tp)
      TermRef(NoPrefix, tp.symbol.asTerm)
    case ExprType(rt) =>
      MethodType(Nil, Nil, this(rt))
    case tp: TypeProxy =>
      this(tp.underlying)
    case AndType(tp1, tp2) =>
      erasedGlb(this(tp1), this(tp2), isJava)
    case OrType(tp1, tp2) =>
      ctx.typeComparer.orType(this(tp1), this(tp2), erased = true)
    case tp: MethodType =>
      val paramErasure = erasureFn(tp.isJava, isSemi, isConstructor, wildcardOK)(_)
      val formals = tp.paramTypes.mapConserve(paramErasure)
      eraseResult(tp.resultType) match {
        case rt: MethodType =>
          tp.derivedMethodType(tp.paramNames ++ rt.paramNames, formals ++ rt.paramTypes, rt.resultType)
        case rt =>
          tp.derivedMethodType(tp.paramNames, formals, rt)
      }
    case tp: PolyType =>
      this(tp.resultType) match {
        case rt: MethodType => rt
        case rt => MethodType(Nil, Nil, rt)
      }
    case tp @ ClassInfo(pre, cls, classParents, decls, _) =>
      if (cls is Package) tp
      else {
        def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef]
        val parents: List[TypeRef] =
          if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
          else if (cls eq defn.ArrayClass) defn.ObjectClass.typeRef :: Nil
          else removeLaterObjects(classParents.mapConserve(eraseTypeRef))
        tp.derivedClassInfo(NoPrefix, parents, decls, this(tp.selfType))
          // can't replace selftype by NoType because this would lose the sourceModule link
      }
    case NoType | NoPrefix | ErrorType =>
      tp
    case tp: WildcardType if wildcardOK =>
      tp
  }

  private def eraseArray(tp: RefinedType)(implicit ctx: Context) = {
    val defn.ArrayType(elemtp) = tp
    if (elemtp derivesFrom defn.NullClass) defn.ObjectArrayType
    else if (isUnboundedGeneric(elemtp)) defn.ObjectType
    else defn.ArrayType(this(elemtp))
  }

  private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type =
    unsupported("eraseDerivedValueClass")

  private def eraseNormalClassRef(tref: TypeRef)(implicit ctx: Context): Type = {
    val cls = tref.symbol.asClass
    (if (cls.owner is Package) normalizeClass(cls) else cls).typeRef
  }

  private def eraseResult(tp: Type)(implicit ctx: Context): Type = tp match {
    case tp: TypeRef =>
      val sym = tp.typeSymbol
      if (sym eq defn.UnitClass) sym.typeRef
      else if (sym.isDerivedValueClass) eraseNormalClassRef(tp)
      else this(tp)
    case RefinedType(parent, _) if !(parent isRef defn.ArrayClass) =>
      eraseResult(parent)
    case _ =>
      this(tp)
  }

  private 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
  }

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

  /** The name of the type as it is used in `Signature`s.
   *  Need to ensure correspondence with erasure!
   */
  def sigName(tp: Type)(implicit ctx: Context): TypeName = tp match {
    case tp: TypeRef =>
      val sym = tp.symbol
      if (!sym.isClass) sigName(tp.info)
      else if (sym.isDerivedValueClass) sigName(eraseDerivedValueClassRef(tp))
      else normalizeClass(sym.asClass).fullName.asTypeName
    case defn.ArrayType(elem) =>
      sigName(elem) ++ "[]"
    case tp: TypeBounds =>
      sigName(tp.hi)
    case ExprType(rt) =>
      sigName(defn.FunctionType(Nil, rt))
    case tp: TypeProxy =>
      sigName(tp.underlying)
    case ErrorType | WildcardType =>
      tpnme.WILDCARD
    case tp: WildcardType =>
      sigName(tp.optBounds)
    case _ =>
      val erased = this(tp)
      assert(erased ne tp, tp)
      sigName(erased)
  }
}