From db4ef5b78be2fb7f954d82a972ab70df28acccfa Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 30 Oct 2013 08:42:53 +0100 Subject: Avoid needless TypeRef allocation during erasure. - ThisType(some.package) need not be erased, we can let that prefix through unchanged and avoid churning through allocations. - Sharpen the condition in `rebindInnerClass`. The answer to the deleted comment in the code, is "anonymous and local classes", which are specifically excluded in the new formulation. - Finally, detect if erasure of the TypeRef is an identity and reuse the original. Waste not, want not. To expand on the first point, here is what used to happen during erasure: scala> val scalaPack = typeOf[Predef.type].prefix scalaPack: $r.intp.global.Type = scala.type scala> typeDeconstruct.show(scalaPack) res19: String = ThisType(package scala) scala> typeDeconstruct.show(erasure.scalaErasure(scalaPack)) res20: String = TypeRef(TypeSymbol(final class scala extends )) Showing one step of the erasure type map: scala> typeDeconstruct.show(scalaPack.asInstanceOf[SubType].underlying.typeOfThis) res21: String = SingleType(pre = ThisType(package ), package scala) --- .../scala/tools/nsc/transform/Erasure.scala | 2 +- src/reflect/scala/reflect/internal/Symbols.scala | 2 +- .../scala/reflect/internal/transform/Erasure.scala | 28 +++++++++++++--------- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index a4854dfbeb..57f3da839a 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -1043,7 +1043,7 @@ abstract class Erasure extends AddInterfaces case Literal(ct) if ct.tag == ClazzTag && ct.typeValue.typeSymbol != definitions.UnitClass => val erased = ct.typeValue match { - case TypeRef(pre, clazz, args) if clazz.isDerivedValueClass => scalaErasure.eraseNormalClassRef(pre, clazz) + case tr @ TypeRef(pre, clazz, args) if clazz.isDerivedValueClass => scalaErasure.eraseNormalClassRef(tr) case tpe => specialScalaErasure(tpe) } treeCopy.Literal(tree, Constant(erased)) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index ba785c14bd..bd17c18119 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -885,7 +885,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Is this symbol a constant? */ final def isConstant: Boolean = isStable && isConstantType(tpe.resultType) - /** Is this class nested in another class or module (not a package)? */ + /** Is this class nested in another class or module (not a package). Includes locally defined classes. */ def isNestedClass = false /** Is this class locally defined? diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 3eb3a4cdf4..6a6668ed05 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -73,9 +73,8 @@ trait Erasure { // included (use pre.baseType(cls.owner)). // // This requires that cls.isClass. - protected def rebindInnerClass(pre: Type, cls: Symbol): Type = { - if (cls.owner.isClass) cls.owner.tpe_* else pre // why not cls.isNestedClass? - } + protected def rebindInnerClass(pre: Type, cls: Symbol): Type = + if (!cls.isTopLevel && !cls.isLocal) cls.owner.tpe_* else pre /** The type of the argument of a value class reference after erasure * This method needs to be called at a phase no later than erasurephase @@ -104,14 +103,21 @@ trait Erasure { abstract class ErasureMap extends TypeMap { def mergeParents(parents: List[Type]): Type - def eraseNormalClassRef(pre: Type, clazz: Symbol): Type = - typeRef(apply(rebindInnerClass(pre, clazz)), clazz, List()) // #2585 + def eraseNormalClassRef(tref: TypeRef): Type = { + val TypeRef(pre, clazz, args) = tref + val pre1 = apply(rebindInnerClass(pre, clazz)) + val args1 = Nil + if ((pre eq pre1) && (args eq args1)) tref // OPT + else typeRef(pre1, clazz, args1) // #2585 + } protected def eraseDerivedValueClassRef(tref: TypeRef): Type = erasedValueClassArg(tref) def apply(tp: Type): Type = tp match { case ConstantType(_) => tp + case st: ThisType if st.sym.isPackageClass => + tp case st: SubType => apply(st.supertype) case tref @ TypeRef(pre, sym, args) => @@ -123,7 +129,7 @@ trait Erasure { else if (sym == UnitClass) BoxedUnitTpe else if (sym.isRefinementClass) apply(mergeParents(tp.parents)) else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tref) - else if (sym.isClass) eraseNormalClassRef(pre, sym) + else if (sym.isClass) eraseNormalClassRef(tref) else apply(sym.info asSeenFrom (pre, sym.owner)) // alias type or abstract type case PolyType(tparams, restpe) => apply(restpe) @@ -151,7 +157,7 @@ trait Erasure { } def applyInArray(tp: Type): Type = tp match { - case TypeRef(pre, sym, args) if (sym.isDerivedValueClass) => eraseNormalClassRef(pre, sym) + case tref @ TypeRef(_, sym, _) if sym.isDerivedValueClass => eraseNormalClassRef(tref) case _ => apply(tp) } } @@ -274,11 +280,11 @@ trait Erasure { } object boxingErasure extends ScalaErasureMap { - override def eraseNormalClassRef(pre: Type, clazz: Symbol) = - if (isPrimitiveValueClass(clazz)) boxedClass(clazz).tpe - else super.eraseNormalClassRef(pre, clazz) + override def eraseNormalClassRef(tref: TypeRef) = + if (isPrimitiveValueClass(tref.sym)) boxedClass(tref.sym).tpe + else super.eraseNormalClassRef(tref) override def eraseDerivedValueClassRef(tref: TypeRef) = - super.eraseNormalClassRef(tref.pre, tref.sym) + super.eraseNormalClassRef(tref) } /** The intersection dominator (SLS 3.7) of a list of types is computed as follows. -- cgit v1.2.3