From 97d89afc4769c4badb42284c9b5d97b663f870f6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 25 Aug 2014 19:13:34 +0200 Subject: Fixes to erasure Makes erasure pass the test suite. Erasure is not yet turned turned on by default, because TestNonCyclic fails with a stale symbol error. The problem is that This types are coupled to Symbols and therefore don't reload. This is a problem is This types refer to static symbols that get recompiled. We either have to drop using This types for static references, or redefine thme so that can be reloaded. --- src/dotty/tools/dotc/TypeErasure.scala | 70 +++++++++++++------------- src/dotty/tools/dotc/core/Definitions.scala | 4 +- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- src/dotty/tools/dotc/core/TypeComparer.scala | 5 +- src/dotty/tools/dotc/core/Types.scala | 26 +++++----- src/dotty/tools/dotc/transform/Erasure.scala | 16 +++--- 6 files changed, 60 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index b65f2889f..c8c54ed03 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -109,6 +109,28 @@ object TypeErasure { } 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._ @@ -127,15 +149,15 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild * - 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 term ref p.x, the type # 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 = + * - For a typeref P.C where C refers to a class, # 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 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. @@ -164,14 +186,12 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild 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)) + erasedGlb(this(tp1), this(tp2), isJava) case OrType(tp1, tp2) => ctx.typeComparer.orType(this(tp1), this(tp2), erased = true) case tp: MethodType => @@ -196,7 +216,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild 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)) + 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 @@ -215,9 +236,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild 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)) + 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 { @@ -247,28 +267,6 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild 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! */ diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 9018d4015..304d9853c 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -377,9 +377,9 @@ class Definitions { } object ArrayType { - def apply(elem: Type) = + def apply(elem: Type)(implicit ctx: Context) = ArrayClass.typeRef.appliedTo(elem :: Nil) - def unapply(tp: Type) = tp.dealias match { + def unapply(tp: Type)(implicit ctx: Context) = tp.dealias match { case at: RefinedType if (at isRef ArrayClass) && at.argInfos.length == 1 => Some(at.argInfos.head) case _ => None } diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 7ba6bbec6..63ce7f756 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -922,7 +922,7 @@ object SymDenotations { /** The type parameters of this class */ override final def typeParams(implicit ctx: Context): List[TypeSymbol] = { def computeTypeParams = { - if (ctx.phase.erasedTypes && (this ne defn.ArrayClass)) Nil + if (ctx.phase.erasedTypes && (symbol ne defn.ArrayClass)) Nil else if (this ne initial) initial.asSymDenotation.typeParams else decls.filter(sym => (sym is TypeParam) && sym.owner == symbol).asInstanceOf[List[TypeSymbol]] diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 4d818cc6f..29f6dda69 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -11,7 +11,7 @@ import printing.Disambiguation.disambiguated import util.{Stats, DotClass, SimpleMap} import config.Config import config.Printers._ -import TypeErasure.erasedLub +import TypeErasure.{erasedLub, erasedGlb} /** Provides methods to compare types. */ @@ -1066,12 +1066,13 @@ class TypeComparer(initctx: Context) extends DotClass { * Such TypeBounds can also be arbitrarily instantiated. In both cases we need to * make sure that such types do not actually arise in source programs. */ - final def andType(tp1: Type, tp2: Type) = ctx.traceIndented(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true) { + final def andType(tp1: Type, tp2: Type, erased: Boolean = ctx.erasedTypes) = ctx.traceIndented(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true) { val t1 = distributeAnd(tp1, tp2) if (t1.exists) t1 else { val t2 = distributeAnd(tp2, tp1) if (t2.exists) t2 + else if (erased) erasedGlb(tp1, tp2, isJava = false) else { //if (isHKRef(tp1)) tp2 //else if (isHKRef(tp2)) tp1 diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index a8bfe61e0..e59c28ca2 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -21,7 +21,7 @@ import ast.tpd._ import ast.TreeTypeMap import printing.Texts._ import ast.untpd -import transform.Erasure +import dotty.tools.dotc.transform.Erasure import printing.Printer import Hashable._ import Uniques._ @@ -2183,17 +2183,17 @@ object Types { def selfType(implicit ctx: Context): Type = { if (selfTypeCache == null) { def fullRef = fullyAppliedRef(cls.typeRef, cls.typeParams) - selfTypeCache = - if (ctx.erasedTypes) fullRef - else selfInfo match { - case NoType => - fullRef - case tp: Type => - if (cls is Module) tp else AndType(tp, fullRef) - case self: Symbol => - assert(!(cls is Module)) - AndType(self.info, fullRef) - } + def withFullRef(tp: Type): Type = + if (ctx.erasedTypes) fullRef else AndType(tp, fullRef) + selfTypeCache = selfInfo match { + case NoType => + fullRef + case tp: Type => + if (cls is Module) tp else withFullRef(tp) + case self: Symbol => + assert(!(cls is Module)) + withFullRef(self.info) + } } selfTypeCache } @@ -2210,7 +2210,7 @@ object Types { } def rebase(tp: Type)(implicit ctx: Context): Type = - if ((prefix eq cls.owner.thisType) || !cls.owner.isClass) tp + if ((prefix eq cls.owner.thisType) || !cls.owner.isClass || ctx.erasedTypes) tp else tp.substThis(cls.owner.asClass, prefix) private var typeRefCache: Type = null diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 53e253c69..1efc6b53c 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -175,12 +175,13 @@ object Erasure { * e -> unbox(e, PT) if `PT` is a primitive type and `e` is not of primitive type * e -> cast(e, PT) otherwise */ - def adaptToType(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { - def makeConformant(tpw: Type): Tree = tpw match { - case MethodType(Nil, _) => + def adaptToType(tree: Tree, pt: Type)(implicit ctx: Context): Tree = + if (pt.isInstanceOf[FunProto]) tree + else tree.tpe.widen match { + case MethodType(Nil, _) if tree.isTerm => adaptToType(tree.appliedToNone, pt) - case _ => - if (pt.isInstanceOf[ProtoType]) + case tpw => + if (pt.isInstanceOf[ProtoType] || tree.tpe <:< pt) tree else if (tpw.isErasedValueType) adaptToType(box(tree), pt) @@ -193,9 +194,6 @@ object Erasure { else cast(tree, pt) } - if ((pt.isInstanceOf[FunProto]) || tree.tpe <:< pt) tree - else makeConformant(tree.tpe.widen) - } } class Typer extends typer.ReTyper with NoChecking { @@ -254,7 +252,7 @@ object Erasure { else if (qual.tpe.derivesFrom(sym.owner) || qual.isInstanceOf[Super]) select(qual, sym) else if (sym.owner eq defn.ArrayClass) - selectArrayMember(qual, erasure(tree.qualifier.typeOpt.widen)) + selectArrayMember(qual, erasure(tree.qualifier.typeOpt.widen.finalResultType)) else recur(cast(qual, sym.owner.typeRef)) } -- cgit v1.2.3