diff options
Diffstat (limited to 'src/dotty/tools')
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 10 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Symbols.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeErasure.scala | 38 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/Erasure.scala | 37 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/TypeTestsCasts.scala | 2 |
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) |