aboutsummaryrefslogtreecommitdiff
path: root/src/dotty
diff options
context:
space:
mode:
authorGuillaume Martres <smarter@ubuntu.com>2015-03-18 23:56:19 +0100
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2015-05-01 13:26:23 +0200
commit5dec4ce8a64d44ee602c09d468414b13eecba389 (patch)
tree3df31764445c447029d2114e25c95d83a60e4c0a /src/dotty
parent391c80c4dfb2489e4098af33265b22332ef3d5f1 (diff)
downloaddotty-5dec4ce8a64d44ee602c09d468414b13eecba389.tar.gz
dotty-5dec4ce8a64d44ee602c09d468414b13eecba389.tar.bz2
dotty-5dec4ce8a64d44ee602c09d468414b13eecba389.zip
Erasure: properly erase value classes
There are three ways to erase a value class: - In most case, it should be semi-erased to an ErasedValueType, which will be fully erased to its underlying type in ElimErasedValueType. This corresponds to semiEraseVCs = true in TypeErasure. - In a few cases, it should be erased like a normal class, so far this seems to be necessary for: * The return type of a constructor * The underlying type of a ThisType * TypeTree nodes inside New nodes * TypeApply nodes * Arrays In these cases, we set semiEraseVCs = false - When calling `sigName` it should be erased to its underlying type. This commit implements all these cases. Note that this breaks most tests because ElimErasedValueType has not been implemented yet, it is part of the next commit.
Diffstat (limited to 'src/dotty')
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala10
-rw-r--r--src/dotty/tools/dotc/core/Symbols.scala8
-rw-r--r--src/dotty/tools/dotc/core/TypeErasure.scala38
-rw-r--r--src/dotty/tools/dotc/transform/Erasure.scala37
-rw-r--r--src/dotty/tools/dotc/transform/TypeTestsCasts.scala2
5 files changed, 68 insertions, 27 deletions
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index bcd46810e..14be606a1 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -1447,10 +1447,16 @@ object SymDenotations {
def inCache(tp: Type) = baseTypeRefCache.containsKey(tp)
- /** Can't cache types containing type variables which are uninstantiated
- * or whose instances can change, depending on typerstate.
+ /** We cannot cache:
+ * - type variables which are uninstantiated or whose instances can
+ * change, depending on typerstate.
+ * - types where the underlying type is an ErasedValueType, because
+ * this underlying type will change after ElimErasedValueType,
+ * and this changes subtyping relations. As a shortcut, we do not
+ * cache ErasedValueType at all.
*/
def isCachable(tp: Type): Boolean = tp match {
+ case _: TypeErasure.ErasedValueType => false
case tp: TypeVar => tp.inst.exists && inCache(tp.inst)
case tp: TypeProxy => inCache(tp.underlying)
case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2)
diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala
index 9f18e723c..2b91efbcd 100644
--- a/src/dotty/tools/dotc/core/Symbols.scala
+++ b/src/dotty/tools/dotc/core/Symbols.scala
@@ -434,14 +434,6 @@ object Symbols {
/** If this symbol satisfies predicate `p` this symbol, otherwise `NoSymbol` */
def filter(p: Symbol => Boolean): Symbol = if (p(this)) this else NoSymbol
- /** Is this symbol a user-defined value class? */
- final def isDerivedValueClass(implicit ctx: Context): Boolean = {
- this.derivesFrom(defn.AnyValClass)(ctx.withPhase(denot.validFor.firstPhaseId))
- // Simulate ValueClasses.isDerivedValueClass
- false // will migrate to ValueClasses.isDerivedValueClass;
- // unsupported value class code will continue to use this stub while it exists
- }
-
/** The current name of this symbol */
final def name(implicit ctx: Context): ThisName = denot.name.asInstanceOf[ThisName]
diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala
index 56b50c74a..1ea63465a 100644
--- a/src/dotty/tools/dotc/core/TypeErasure.scala
+++ b/src/dotty/tools/dotc/core/TypeErasure.scala
@@ -5,11 +5,13 @@ package core
import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._, Flags.JavaDefined
import Uniques.unique
import dotc.transform.ExplicitOuter._
+import dotc.transform.ValueClasses._
import typer.Mode
import util.DotClass
/** Erased types are:
*
+ * ErasedValueType
* TypeRef(prefix is ignored, denot is ClassDenotation)
* TermRef(prefix is ignored, denot is SymDenotation)
* JavaArrayType
@@ -30,8 +32,12 @@ object TypeErasure {
/** A predicate that tests whether a type is a legal erased type. Only asInstanceOf and
* isInstanceOf may have types that do not satisfy the predicate.
+ * ErasedValueType is considered an erased type because it is valid after Erasure (it is
+ * eliminated by ElimErasedValueType).
*/
def isErasedType(tp: Type)(implicit ctx: Context): Boolean = tp match {
+ case _: ErasedValueType =>
+ true
case tp: TypeRef =>
tp.symbol.isClass && tp.symbol != defn.AnyClass
case _: TermRef =>
@@ -283,10 +289,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
* - For any other type, exception.
*/
private def apply(tp: Type)(implicit ctx: Context): Type = tp match {
+ case _: ErasedValueType =>
+ tp
case tp: TypeRef =>
val sym = tp.symbol
if (!sym.isClass) this(tp.info)
- else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tp)
+ else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp)
else eraseNormalClassRef(tp)
case tp: RefinedType =>
val parent = tp.parent
@@ -295,7 +303,9 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
case tp: TermRef =>
this(tp.widen)
case tp: ThisType =>
- this(tp.cls.typeRef)
+ def thisTypeErasure(tpToErase: Type) =
+ erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(tpToErase)
+ thisTypeErasure(tp.cls.typeRef)
case SuperType(thistpe, supertpe) =>
SuperType(this(thistpe), this(supertpe))
case ExprType(rt) =>
@@ -307,7 +317,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
case OrType(tp1, tp2) =>
ctx.typeComparer.orType(this(tp1), this(tp2), erased = true)
case tp: MethodType =>
- val paramErasure = erasureFn(tp.isJava, semiEraseVCs, isConstructor, wildcardOK)(_)
+ def paramErasure(tpToErase: Type) =
+ erasureFn(tp.isJava, semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
val formals = tp.paramTypes.mapConserve(paramErasure)
eraseResult(tp.resultType) match {
case rt: MethodType =>
@@ -345,9 +356,11 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
private def eraseArray(tp: RefinedType)(implicit ctx: Context) = {
val defn.ArrayType(elemtp) = tp
+ def arrayErasure(tpToErase: Type) =
+ erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(tpToErase)
if (elemtp derivesFrom defn.NullClass) JavaArrayType(defn.ObjectType)
else if (isUnboundedGeneric(elemtp)) defn.ObjectType
- else JavaArrayType(this(elemtp))
+ else JavaArrayType(arrayErasure(elemtp))
}
/** The erasure of a symbol's info. This is different from `apply` in the way `ExprType`s are
@@ -365,8 +378,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
case tp => this(tp)
}
- private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type =
- unsupported("eraseDerivedValueClass")
+ private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = {
+ val cls = tref.symbol.asClass
+ val underlying = underlyingOfValueClass(cls)
+ ErasedValueType(cls, erasure(underlying))
+ }
+
private def eraseNormalClassRef(tref: TypeRef)(implicit ctx: Context): Type = {
val cls = tref.symbol.asClass
@@ -378,7 +395,10 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
case tp: TypeRef =>
val sym = tp.typeSymbol
if (sym eq defn.UnitClass) sym.typeRef
- else if (sym.isDerivedValueClass) eraseNormalClassRef(tp)
+ // For a value class V, "new V(x)" should have type V for type adaptation to work
+ // correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the return type of a
+ // constructor method should not be semi-erased.
+ else if (isConstructor && isDerivedValueClass(sym)) eraseNormalClassRef(tp)
else this(tp)
case RefinedType(parent, _) if !(parent isRef defn.ArrayClass) =>
eraseResult(parent)
@@ -400,10 +420,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
* Need to ensure correspondence with erasure!
*/
private def sigName(tp: Type)(implicit ctx: Context): TypeName = tp match {
+ case ErasedValueType(_, underlying) =>
+ sigName(underlying)
case tp: TypeRef =>
val sym = tp.symbol
if (!sym.isClass) sigName(tp.info)
- else if (sym.isDerivedValueClass) sigName(eraseDerivedValueClassRef(tp))
+ else if (isDerivedValueClass(sym)) sigName(eraseDerivedValueClassRef(tp))
else normalizeClass(sym.asClass).fullName.asTypeName
case defn.ArrayType(elem) =>
sigName(this(tp))
diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala
index 01f86915d..293059af2 100644
--- a/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/transform/Erasure.scala
@@ -254,18 +254,39 @@ object Erasure extends TypeTestsCasts{
class Typer extends typer.ReTyper with NoChecking {
import Boxing._
- def erasedType(tree: untpd.Tree)(implicit ctx: Context): Type = tree.typeOpt match {
- case tp: TermRef if tree.isTerm => erasedRef(tp)
- case tp => erasure(tp)
- }
+ def erasedType(tree: untpd.Tree, semiEraseVCs: Boolean = true)(implicit ctx: Context): Type =
+ tree.typeOpt match {
+ case tp: TermRef if tree.isTerm => erasedRef(tp)
+ case tp => erasure(tp, semiEraseVCs)
+ }
- override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
+ def promote(tree: untpd.Tree, semiEraseVCs: Boolean)(implicit ctx: Context): tree.ThisTree[Type] = {
assert(tree.hasType)
- val erased = erasedType(tree)
+ val erased = erasedType(tree, semiEraseVCs)
ctx.log(s"promoting ${tree.show}: ${erased.showWithUnderlying()}")
tree.withType(erased)
}
+ override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
+ promote(tree, true)
+ }
+
+ /** When erasing most TypeTrees we should not semi-erase value types.
+ * This is not the case for [[DefDef#tpt]], [[ValDef#tpt]] and [[Typed#tpt]], they
+ * are handled separately by [[typedDefDef]], [[typedValDef]] and [[typedTyped]].
+ */
+ override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = {
+ promote(tree, semiEraseVCs = false)
+ }
+
+ /** This override is only needed to semi-erase type ascriptions */
+ override def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = {
+ val Typed(expr, tpt) = tree
+ val tpt1 = promote(tpt)
+ val expr1 = typed(expr, tpt1.tpe)
+ assignType(untpd.cpy.Typed(tree)(expr1, tpt1), tpt1)
+ }
+
override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal =
if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt)
else super.typedLiteral(tree)
@@ -330,7 +351,7 @@ object Erasure extends TypeTestsCasts{
assert(sym.isConstructor, s"${sym.showLocated}")
select(qual, defn.ObjectClass.info.decl(sym.name).symbol)
}
- else if (qualIsPrimitive && !symIsPrimitive || qual.tpe.isErasedValueType)
+ else if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType)
recur(box(qual))
else if (!qualIsPrimitive && symIsPrimitive)
recur(unbox(qual, sym.owner.typeRef))
@@ -349,7 +370,7 @@ object Erasure extends TypeTestsCasts{
}
override def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context) =
- untpd.Ident(tree.name).withPos(tree.pos).withType(erasedType(tree))
+ untpd.Ident(tree.name).withPos(tree.pos).withType(erasedType(tree, semiEraseVCs = false))
override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =
if (tree.symbol == ctx.owner.enclosingClass || tree.symbol.isStaticOwner) promote(tree)
diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
index 9d827d3e0..b8ba4427c 100644
--- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
+++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
@@ -93,7 +93,7 @@ trait TypeTestsCasts {
else
derivedTree(qual, defn.Any_asInstanceOf, argType)
}
- def erasedArg = erasure(tree.args.head.tpe)
+ def erasedArg = erasure(tree.args.head.tpe, semiEraseVCs = false)
if (sym eq defn.Any_isInstanceOf)
transformIsInstanceOf(qual, erasedArg)
else if (sym eq defn.Any_asInstanceOf)