From 4041c5d590f78323d640c6eec7e370a37a01c416 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 30 Aug 2014 13:02:56 +0200 Subject: Ensure that after erasure all types are erased. Defines a predicate isErasedTypes and checks that all tree types and their widened underlying types are erased. --- src/dotty/tools/dotc/TypeErasure.scala | 38 ++++++++++++++++++++---- src/dotty/tools/dotc/core/TypeApplications.scala | 10 ++----- src/dotty/tools/dotc/core/Types.scala | 10 +++++-- src/dotty/tools/dotc/transform/Erasure.scala | 3 -- src/dotty/tools/dotc/transform/TreeChecker.scala | 14 ++++++++- src/dotty/tools/dotc/typer/Typer.scala | 2 +- 6 files changed, 57 insertions(+), 20 deletions(-) (limited to 'src/dotty/tools') diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 63b4f396b..d2b241e71 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -1,17 +1,18 @@ package dotty.tools.dotc package core -import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Flags.JavaDefined +import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._, Flags.JavaDefined import util.DotClass /** Erased types are: * - * TypeRef(NoPrefix, denot is ClassDenotation) - * TermRef(NoPrefix, denot is SymDenotation) + * TypeRef(prefix is ignored, denot is ClassDenotation) + * TermRef(prefix is ignored, denot is SymDenotation) * JavaArrayType * AnnotatedType - * MethodType ----+-- JavaMethodType - * ClassInfo + * MethodType + * ThisType + * ClassInfo (NoPrefix, ...) * NoType * NoPrefix * WildcardType @@ -22,6 +23,30 @@ import util.DotClass */ 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. + */ + def isErasedType(tp: Type)(implicit ctx: Context): Boolean = tp match { + case tp: TypeRef => + tp.symbol.isClass + case _: TermRef => + true + case JavaArrayType(elem) => + isErasedType(elem) + case AnnotatedType(_, tp) => + isErasedType(tp) + case ThisType(tref) => + isErasedType(tref) + case tp: MethodType => + tp.paramTypes.forall(isErasedType) && isErasedType(tp.resultType) + case tp @ ClassInfo(pre, _, parents, decls, _) => + isErasedType(pre) && parents.forall(isErasedType) //&& decls.forall(sym => isErasedType(sym.info)) && isErasedType(tp.selfType) + case NoType | NoPrefix | WildcardType | ErrorType => + true + case _ => + false + } + case class ErasedValueType(cls: ClassSymbol, underlying: Type) extends CachedGroundType { override def computeHash = doHash(cls, underlying) } @@ -179,6 +204,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild * - 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 a this-type C.this, the type itself. * - 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 @@ -208,6 +234,8 @@ 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 => diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 2c8e9902b..bf756facf 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -41,8 +41,6 @@ import TypeApplications._ /** A decorator that provides methods for modeling type application */ class TypeApplications(val self: Type) extends AnyVal { - def canHaveTypeParams(implicit ctx: Context) = !ctx.erasedTypes || self.isRef(defn.ArrayClass) - /** The type parameters of this type are: * For a ClassInfo type, the type parameters of its class. * For a typeref referring to a class, the type parameters of the class. @@ -187,7 +185,7 @@ class TypeApplications(val self: Type) extends AnyVal { tp } - if (args.isEmpty || !canHaveTypeParams) self + if (args.isEmpty || ctx.erasedTypes) self else { val res = instantiate(self, self) if (isInstantiatedLambda(res)) res.select(tpnme.Apply) else res @@ -278,10 +276,8 @@ class TypeApplications(val self: Type) extends AnyVal { */ def translateParameterized(from: ClassSymbol, to: ClassSymbol)(implicit ctx: Context): Type = if (self.derivesFrom(from)) - if (canHaveTypeParams) - RefinedType(to.typeRef, to.typeParams.head.name, self.member(from.typeParams.head.name).info) - else - to.typeRef + if (ctx.erasedTypes) to.typeRef + else RefinedType(to.typeRef, to.typeParams.head.name, self.member(from.typeParams.head.name).info) else self /** If this is repeated parameter type, its underlying Seq type, diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 42ff62f4d..1cd9c4af9 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1588,8 +1588,9 @@ object Types { final class CachedSuperType(thistpe: Type, supertpe: Type) extends SuperType(thistpe, supertpe) object SuperType { - def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context) = - unique(new CachedSuperType(thistpe, supertpe)) + def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context): Type = + if (ctx.erasedTypes) thistpe + else unique(new CachedSuperType(thistpe, supertpe)) } /** A constant type with single `value`. */ @@ -1684,10 +1685,13 @@ object Types { if (names.isEmpty) parent else make(RefinedType(parent, names.head, infoFns.head), names.tail, infoFns.tail) - def apply(parent: Type, name: Name, infoFn: RefinedType => Type)(implicit ctx: Context): RefinedType = + def apply(parent: Type, name: Name, infoFn: RefinedType => Type)(implicit ctx: Context): RefinedType = { + assert(!ctx.erasedTypes) ctx.base.uniqueRefinedTypes.enterIfNew(new CachedRefinedType(parent, name, infoFn)).checkInst + } def apply(parent: Type, name: Name, info: Type)(implicit ctx: Context): RefinedType = { + assert(!ctx.erasedTypes) ctx.base.uniqueRefinedTypes.enterIfNew(parent, name, info).checkInst } } diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 86151fae2..ba6e1dbe0 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -309,9 +309,6 @@ object Erasure { } } - override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = - promote(tree) - override def ensureNoLocalRefs(block: Block, pt: Type, forcedDefined: Boolean = false)(implicit ctx: Context): Tree = block // optimization, no checking needed, as block symbols do not change. diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 2edaabdf2..bd7e351e1 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -12,6 +12,7 @@ import core.Flags._ import core.Constants._ import core.StdNames._ import core.Decorators._ +import core.TypeErasure.isErasedType import typer._ import typer.ErrorReporting._ import reporting.ThrowingReporter @@ -42,7 +43,7 @@ class TreeChecker { object Checker extends ReTyper { override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = try { - tree match { + val res = tree match { case _: untpd.UnApply => // can't recheck patterns tree.asInstanceOf[tpd.Tree] @@ -66,6 +67,8 @@ class TreeChecker { assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt)) tree1 } + if (ctx.erasedTypes) assertErased(res) + res } catch { case ex: Throwable => println(i"exception while checking $tree of class ${tree.getClass} # ${tree.uniqueId}") @@ -119,6 +122,15 @@ class TreeChecker { tree } } + + def assertErased(tp: Type, tree: Tree = EmptyTree)(implicit ctx: Context): Unit = + assert(isErasedType(tp), i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase}") + + def assertErased(tree: Tree)(implicit ctx: Context): Unit = { + assertErased(tree.typeOpt, tree) + if (!(tree.symbol == defn.Any_isInstanceOf || tree.symbol == defn.Any_asInstanceOf)) + assertErased(tree.typeOpt.widen, tree) + } } object TreeChecker extends TreeChecker \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 89cf0b055..f21528da0 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -834,7 +834,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (tree.isType) typedType(tree)(superCtx) else { val result = typedExpr(tree)(superCtx) - if ((cls is Trait) && result.tpe.classSymbol.isRealClass) + if ((cls is Trait) && result.tpe.classSymbol.isRealClass && !ctx.isAfterTyper) ctx.error(s"trait may not call constructor of ${result.tpe.classSymbol}", tree.pos) result } -- cgit v1.2.3