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]. : [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 } } 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 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 whose symbol is owned by Array: The typeref itself * - 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, the merge of |T1| and |T2| (see mergeAnd) * - 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 _: ThisType => tp case ExprType(rt) => MethodType(Nil, Nil, this(rt)) case tp: TypeProxy => this(tp.underlying) case AndType(tp1, tp2) => mergeAnd(this(tp1), this(tp2)) 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(this(pre), parents, decls, this(tp.selfType)) } 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 sym = tref.symbol if (sym.owner is Package) normalizeClass(sym.asClass).typeRef else tref.derivedSelect(this(tref.prefix)) } 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 } private def mergeAnd(tp1: Type, tp2: Type)(implicit ctx: Context): Type = tp1 match { case defn.ArrayType(elem1) => tp2 match { case defn.ArrayType(elem2) => defn.ArrayType(mergeAnd(elem1, elem2)) 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 } } /** 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) } }