aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/TypeErasure.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-08-20 15:00:51 +0200
committerMartin Odersky <odersky@gmail.com>2014-08-24 17:46:36 +0200
commit1f2619bdc4c7b80c3f63fecbe27e47d5f665931d (patch)
tree0c999de882d79d11308607a02dcdf33878b2c95a /src/dotty/tools/dotc/TypeErasure.scala
parentab0105cf91fa452abae8ef1d0d14634d8d7d4ab8 (diff)
downloaddotty-1f2619bdc4c7b80c3f63fecbe27e47d5f665931d.tar.gz
dotty-1f2619bdc4c7b80c3f63fecbe27e47d5f665931d.tar.bz2
dotty-1f2619bdc4c7b80c3f63fecbe27e47d5f665931d.zip
renaming: core.transform.Erasure -> core.TypeErasure
The name duplications transform.Erasure / core.transform.Erasure caused irregularities in the imports and the smae name was confusing. Besides Erasure was the only class in core.transform, so it seemed better to eliminate the package alltogether.
Diffstat (limited to 'src/dotty/tools/dotc/TypeErasure.scala')
-rw-r--r--src/dotty/tools/dotc/TypeErasure.scala298
1 files changed, 298 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala
new file mode 100644
index 000000000..b65f2889f
--- /dev/null
+++ b/src/dotty/tools/dotc/TypeErasure.scala
@@ -0,0 +1,298 @@
+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
+ }
+}
+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)
+ }
+}