diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-07-11 23:43:46 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-07-11 23:43:46 -0700 |
commit | a5691488311d80d4a07626f4c09289295368ef0c (patch) | |
tree | 1e2d9c947c76af097251f7ee69c6c21187c79568 | |
parent | a0ba7c4a3feae7f9421d55e3e7c0691b02d82be3 (diff) | |
parent | 39fc4aa845dab660d9d360f9b6e9fd82f5d765fe (diff) | |
download | scala-a5691488311d80d4a07626f4c09289295368ef0c.tar.gz scala-a5691488311d80d4a07626f4c09289295368ef0c.tar.bz2 scala-a5691488311d80d4a07626f4c09289295368ef0c.zip |
Merge pull request #878 from adriaanm/topic-polyvalclass
polymorphic value classes
9 files changed, 128 insertions, 49 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 327a864e3b..e378d71944 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -319,7 +319,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def ccon = Class.forName(name).getConstructor(classOf[CharsetDecoder], classOf[Reporter]) try Some(ccon.newInstance(charset.newDecoder(), reporter).asInstanceOf[SourceReader]) - catch { case x: Exception => + catch { case ex: Throwable => globalError("exception while trying to instantiate source reader '" + name + "'") None } @@ -1546,7 +1546,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def compileUnits(units: List[CompilationUnit], fromPhase: Phase) { try compileUnitsInternal(units, fromPhase) - catch { case ex => + catch { case ex: Throwable => val shown = if (settings.verbose.value) { val pw = new java.io.PrintWriter(new java.io.StringWriter) ex.printStackTrace(pw) diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 1276d62995..5115c49c87 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -351,7 +351,7 @@ abstract class Erasure extends AddInterfaces List()) if cast.symbol == Object_asInstanceOf && tpt.tpe.typeSymbol.isDerivedValueClass && - sel.symbol == tpt.tpe.typeSymbol.firstParamAccessor => + sel.symbol == tpt.tpe.typeSymbol.derivedValueClassUnbox => Some(arg) case _ => None @@ -498,7 +498,8 @@ abstract class Erasure extends AddInterfaces ldef setType ldef.rhs.tpe case _ => val tree1 = tree.tpe match { - case ErasedValueType(clazz) => + case ErasedValueType(tref) => + val clazz = tref.sym tree match { case Unboxed(arg) if arg.tpe.typeSymbol == clazz => log("shortcircuiting unbox -> box "+arg); arg @@ -554,25 +555,26 @@ abstract class Erasure extends AddInterfaces ldef setType ldef.rhs.tpe case _ => val tree1 = pt match { - case ErasedValueType(clazz) => + case ErasedValueType(tref) => tree match { case Boxed(arg) if arg.tpe.isInstanceOf[ErasedValueType] => log("shortcircuiting box -> unbox "+arg) arg case _ => + val clazz = tref.sym log("not boxed: "+tree) val tree0 = adaptToType(tree, clazz.tpe) - cast(Apply(Select(tree0, clazz.firstParamAccessor), List()), pt) + cast(Apply(Select(tree0, clazz.derivedValueClassUnbox), List()), pt) } case _ => pt.typeSymbol match { - case UnitClass => - if (treeInfo isExprSafeToInline tree) UNIT - else BLOCK(tree, UNIT) - case x => - assert(x != ArrayClass) - // don't `setType pt` the Apply tree, as the Apply's fun won't be typechecked if the Apply tree already has a type - Apply(unboxMethod(pt.typeSymbol), tree) + case UnitClass => + if (treeInfo isExprSafeToInline tree) UNIT + else BLOCK(tree, UNIT) + case x => + assert(x != ArrayClass) + // don't `setType pt` the Apply tree, as the Apply's fun won't be typechecked if the Apply tree already has a type + Apply(unboxMethod(pt.typeSymbol), tree) } } typedPos(tree.pos)(tree1) @@ -601,7 +603,7 @@ abstract class Erasure extends AddInterfaces * @return the adapted tree */ private def adaptToType(tree: Tree, pt: Type): Tree = { - if (settings.debug.value && pt != WildcardType) + //if (settings.debug.value && pt != WildcardType) log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug if (tree.tpe <:< pt) tree @@ -629,7 +631,7 @@ abstract class Erasure extends AddInterfaces * - `x != y` for != in class Any becomes `!(x equals y)` with equals in class Object. * - x.asInstanceOf[T] becomes x.$asInstanceOf[T] * - x.isInstanceOf[T] becomes x.$isInstanceOf[T] - * - x.isInstanceOf[ErasedValueType(clazz)] becomes x.isInstanceOf[clazz.tpe] + * - x.isInstanceOf[ErasedValueType(tref)] becomes x.isInstanceOf[tref.sym.tpe] * - x.m where m is some other member of Any becomes x.m where m is a member of class Object. * - x.m where x has unboxed value type T and m is not a directly translated member of T becomes T.box(x).m * - x.m where x is a reference type and m is a directly translated member of value type T becomes x.TValue().m @@ -651,12 +653,33 @@ abstract class Erasure extends AddInterfaces atPos(tree.pos)(Apply(Select(qual1, "to" + targClass.name), List())) else */ - if (isPrimitiveValueType(targ.tpe) || isErasedValueType(targ.tpe)) unbox(qual1, targ.tpe) - else tree + if (isPrimitiveValueType(targ.tpe) || isErasedValueType(targ.tpe)) { + val noNullCheckNeeded = targ.tpe match { + case ErasedValueType(tref) => + atPhase(currentRun.erasurePhase) { + isPrimitiveValueClass(erasedValueClassArg(tref).typeSymbol) + } + case _ => + true + } + if (noNullCheckNeeded) unbox(qual1, targ.tpe) + else { + def nullConst = Literal(Constant(null)) setType NullClass.tpe + val untyped = +// util.trace("new asinstanceof test") { + gen.evalOnce(qual1, context.owner, context.unit) { qual => + If(Apply(Select(qual(), nme.eq), List(Literal(Constant(null)) setType NullClass.tpe)), + Literal(Constant(null)) setType targ.tpe, + unbox(qual(), targ.tpe)) + } +// } + typed(untyped) + } + } else tree case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) if tree.symbol == Any_isInstanceOf => targ.tpe match { - case ErasedValueType(clazz) => targ.setType(clazz.tpe) + case ErasedValueType(tref) => targ.setType(tref.sym.tpe) case _ => } tree @@ -711,17 +734,22 @@ abstract class Erasure extends AddInterfaces val tree1 = try { tree match { case InjectDerivedValue(arg) => - val clazz = tree.symbol - val result = typed1(arg, mode, underlyingOfValueClass(clazz)) setType ErasedValueType(clazz) - log("transforming inject "+arg+":"+underlyingOfValueClass(clazz)+"/"+ErasedValueType(clazz)+" = "+result) - return result + (tree.attachments.get[TypeRefAttachment]: @unchecked) match { + case Some(itype) => + val tref = itype.tpe + val argPt = atPhase(currentRun.erasurePhase)(erasedValueClassArg(tref)) + log(s"transforming inject $arg -> $tref/$argPt") + val result = typed(arg, mode, argPt) + log(s"transformed inject $arg -> $tref/$argPt = $result:${result.tpe}") + return result setType ErasedValueType(tref) + } case _ => - super.typed1(adaptMember(tree), mode, pt) + super.typed1(adaptMember(tree), mode, pt) } } catch { case er: TypeError => - Console.println("exception when typing " + tree) + Console.println("exception when typing " + tree+"/"+tree.getClass) Console.println(er.msg + " in file " + context.owner.sourceFile) er.printStackTrace abort("unrecoverable error") @@ -731,6 +759,7 @@ abstract class Erasure extends AddInterfaces finally throw ex throw ex } + def adaptCase(cdef: CaseDef): CaseDef = { val newCdef = deriveCaseDef(cdef)(adaptToType(_, tree1.tpe)) newCdef setType newCdef.body.tpe @@ -970,8 +999,11 @@ abstract class Erasure extends AddInterfaces else tree + case Apply(Select(New(tpt), nme.CONSTRUCTOR), List(arg)) if (tpt.tpe.typeSymbol.isDerivedValueClass) => - InjectDerivedValue(arg) setSymbol tpt.tpe.typeSymbol +// println("inject derived: "+arg+" "+tpt.tpe) + InjectDerivedValue(arg) addAttachment //@@@ setSymbol tpt.tpe.typeSymbol + new TypeRefAttachment(tree.tpe.asInstanceOf[TypeRef]) case Apply(fn, args) => def qualifier = fn match { case Select(qual, _) => qual @@ -1125,4 +1157,6 @@ abstract class Erasure extends AddInterfaces } } } + + private class TypeRefAttachment(val tpe: TypeRef) } diff --git a/src/compiler/scala/tools/nsc/transform/PostErasure.scala b/src/compiler/scala/tools/nsc/transform/PostErasure.scala index ef158a71f6..999d00520d 100644 --- a/src/compiler/scala/tools/nsc/transform/PostErasure.scala +++ b/src/compiler/scala/tools/nsc/transform/PostErasure.scala @@ -21,7 +21,8 @@ trait PostErasure extends InfoTransform with TypingTransformers { object elimErasedValueType extends TypeMap { def apply(tp: Type) = tp match { - case ErasedValueType(clazz) => erasure.underlyingOfValueClass(clazz) + case ErasedValueType(tref) => + atPhase(currentRun.erasurePhase)(erasure.erasedValueClassArg(tref)) case _ => mapOver(tp) } } @@ -38,7 +39,7 @@ trait PostErasure extends InfoTransform with TypingTransformers { acc), List()) if atPhase(currentRun.erasurePhase) { tpt.tpe.typeSymbol.isDerivedValueClass && - sel.symbol == tpt.tpe.typeSymbol.firstParamAccessor + sel.symbol == tpt.tpe.typeSymbol.derivedValueClassUnbox } => if (settings.debug.value) log("Removing "+tree+" -> "+arg) arg diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index f01e095856..5465a3b47f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -196,14 +196,14 @@ trait SyntheticMethods extends ast.TreeDSL { * (this.underlying == that.underlying */ def equalsDerivedValueClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => - equalsCore(m, List(clazz.firstParamAccessor)) + equalsCore(m, List(clazz.derivedValueClassUnbox)) } /** The hashcode method for value classes * def hashCode(): Int = this.underlying.hashCode */ def hashCodeDerivedValueClassMethod: Tree = createMethod(nme.hashCode_, Nil, IntClass.tpe) { m => - Select(mkThisSelect(clazz.firstParamAccessor), nme.hashCode_) + Select(mkThisSelect(clazz.derivedValueClassUnbox), nme.hashCode_) } /** The _1, _2, etc. methods to implement ProductN. diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index b9fe269e43..5241974793 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1379,7 +1379,7 @@ trait Typers extends Modes with Adaptations with Tags { for (stat <- body) if (!treeInfo.isAllowedInUniversalTrait(stat) && !isUnderlyingAcc(stat.symbol)) unit.error(stat.pos, - if (stat.symbol hasFlag PARAMACCESSOR) "illegal parameter for value class" + if (stat.symbol != null && (stat.symbol hasFlag PARAMACCESSOR)) "illegal parameter for value class" else "this statement is not allowed in value class: " + stat) case x => unit.error(clazz.pos, "value class needs to have exactly one public val parameter") diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 957202e448..04fa01c6f3 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -2239,10 +2239,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => base.info.decl(sname) filter (_.hasAccessorFlag) } - /** Return the accessor method of the first parameter of this class. + /** If this is a derived value class, return its unbox method * or NoSymbol if it does not exist. */ - def firstParamAccessor: Symbol = NoSymbol + def derivedValueClassUnbox: Symbol = NoSymbol /** The case module corresponding to this case class * @pre case class is a member of some other class or package @@ -3146,8 +3146,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => clone } - override def firstParamAccessor = - info.decls.find(_ hasAllFlags PARAMACCESSOR | METHOD) getOrElse NoSymbol + override def derivedValueClassUnbox = + (info.decl(nme.unbox)) orElse + (info.decls.find(_ hasAllFlags PARAMACCESSOR | METHOD) getOrElse + NoSymbol) private[this] var childSet: Set[Symbol] = Set() override def children = childSet diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 56cc265e48..0b3e125053 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -66,7 +66,7 @@ import util.Statistics // inst is the instantiation and constr is a list of bounds. case DeBruijnIndex(level, index) // for dependent method types: a type referring to a method parameter. - case ErasedValueType(tp) + case ErasedValueType(clazz, underlying) // only used during erasure of derived value classes. */ @@ -3305,16 +3305,21 @@ trait Types extends api.Types { self: SymbolTable => } } - abstract case class ErasedValueType(sym: Symbol) extends Type { - override def safeToString = sym.name+"$unboxed" + /** A temporary type representing the reasure of a user-defined value type. + * Created during phase reasure, elimintaed again in posterasure. + * @param sym The value class symbol + * @param underlying The underlying type before erasure + */ + abstract case class ErasedValueType(original: TypeRef) extends Type { + override def safeToString = "ErasedValueType("+original+")" } - final class UniqueErasedValueType(sym: Symbol) extends ErasedValueType(sym) with UniqueType + final class UniqueErasedValueType(original: TypeRef) extends ErasedValueType(original) with UniqueType object ErasedValueType { - def apply(sym: Symbol): Type = { - assert(sym ne NoSymbol, "ErasedValueType cannot be NoSymbol") - unique(new UniqueErasedValueType(sym)) + def apply(original: TypeRef): Type = { + assert(original.sym ne NoSymbol, "ErasedValueType over NoSymbol") + unique(new UniqueErasedValueType(original)) } } diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 5beec70d62..368d55a59c 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -2,7 +2,7 @@ package scala.reflect package internal package transform -import Flags.PARAMACCESSOR +import Flags.{PARAMACCESSOR, METHOD} trait Erasure { @@ -72,8 +72,37 @@ trait Erasure { if (cls.owner.isClass) cls.owner.tpe else pre // why not cls.isNestedClass? } + def unboxDerivedValueClassMethod(clazz: Symbol): Symbol = + (clazz.info.decl(nme.unbox)) orElse + (clazz.info.decls.find(_ hasAllFlags PARAMACCESSOR | METHOD) getOrElse + NoSymbol) + def underlyingOfValueClass(clazz: Symbol): Type = - clazz.firstParamAccessor.tpe.resultType + clazz.derivedValueClassUnbox.tpe.resultType + + /** 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 + */ + def erasedValueClassArg(tref: TypeRef): Type = { + assert(!phase.erasedTypes) + val clazz = tref.sym + if (valueClassIsParametric(clazz)) { + val underlying = tref.memberType(clazz.derivedValueClassUnbox).resultType + boxingErasure(underlying) + } else { + scalaErasure(underlyingOfValueClass(clazz)) + } + } + + /** Does this vakue class have an underlying type that's a type parameter of + * the class itself? + * This method needs to be called at a phase no later than erasurephase + */ + def valueClassIsParametric(clazz: Symbol): Boolean = { + assert(!phase.erasedTypes) + clazz.typeParams contains + clazz.derivedValueClassUnbox.tpe.resultType.normalize.typeSymbol + } abstract class ErasureMap extends TypeMap { private lazy val ObjectArray = arrayType(ObjectClass.tpe) @@ -84,15 +113,14 @@ trait Erasure { def eraseNormalClassRef(pre: Type, clazz: Symbol): Type = typeRef(apply(rebindInnerClass(pre, clazz)), clazz, List()) // #2585 - protected def eraseDerivedValueClassRef(clazz: Symbol): Type = - scalaErasure(underlyingOfValueClass(clazz)) + protected def eraseDerivedValueClassRef(tref: TypeRef): Type = erasedValueClassArg(tref) def apply(tp: Type): Type = tp match { case ConstantType(_) => tp case st: SubType => apply(st.supertype) - case TypeRef(pre, sym, args) => + case tref @ TypeRef(pre, sym, args) => if (sym == ArrayClass) if (unboundedGenericArrayLevel(tp) == 1) ObjectClass.tpe else if (args.head.typeSymbol.isBottomClass) ObjectArray @@ -100,7 +128,7 @@ trait Erasure { else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) ErasedObject else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass) else if (sym.isRefinementClass) apply(mergeParents(tp.parents)) - else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(sym) + else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tref) else if (sym.isClass) eraseNormalClassRef(pre, sym) else apply(sym.info) // alias type or abstract type case PolyType(tparams, restpe) => @@ -236,7 +264,8 @@ trait Erasure { * are then later converted to the underlying parameter type in phase posterasure. */ object specialScalaErasure extends ScalaErasureMap { - override def eraseDerivedValueClassRef(clazz: Symbol): Type = ErasedValueType(clazz) + override def eraseDerivedValueClassRef(tref: TypeRef): Type = + ErasedValueType(tref) } object javaErasure extends JavaErasureMap @@ -251,6 +280,14 @@ 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 eraseDerivedValueClassRef(tref: TypeRef) = + super.eraseNormalClassRef(tref.pre, tref.sym) + } + /** The intersection dominator (SLS 3.7) of a list of types is computed as follows. * * - If the list contains one or more occurrences of scala.Array with diff --git a/src/reflect/scala/reflect/internal/util/Statistics.scala b/src/reflect/scala/reflect/internal/util/Statistics.scala index 3a31c2858b..e503d812e6 100644 --- a/src/reflect/scala/reflect/internal/util/Statistics.scala +++ b/src/reflect/scala/reflect/internal/util/Statistics.scala @@ -169,7 +169,7 @@ quant) nanos = nanos0 + System.nanoTime() - start timings += 1 } - protected def show(ns: Long) = s"${ns/1000}ms" + protected def show(ns: Long) = s"${ns/1000000}ms" override def toString = s"$timings spans, ${show(nanos)}" } |