From 52c99f57ef41c436719818b8e3860e3bfbf023e2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 13 Feb 2012 00:02:39 +0100 Subject: All steps of value class proposal implemented. Most restrictions are now enforced. Super calls and specialized still missing. --- src/compiler/scala/reflect/internal/Symbols.scala | 87 +++++++++------- .../scala/reflect/internal/transform/Erasure.scala | 25 ++--- src/compiler/scala/tools/nsc/Global.scala | 24 +++-- src/compiler/scala/tools/nsc/ast/TreeInfo.scala | 6 +- .../scala/tools/nsc/transform/Erasure.scala | 113 ++++++++++++++------- .../tools/nsc/transform/ExtensionMethods.scala | 3 +- .../tools/nsc/typechecker/SyntheticMethods.scala | 108 ++++++++++++++------ .../scala/tools/nsc/typechecker/Typers.scala | 32 +++++- 8 files changed, 258 insertions(+), 140 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index e448ef86f6..89af10283b 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -17,7 +17,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => import definitions._ protected var ids = 0 - + val emptySymbolArray = new Array[Symbol](0) def symbolCount = ids // statistics @@ -38,14 +38,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => nextexid += 1 newTypeName("_" + nextexid + suffix) } - + // Set the fields which point companions at one another. Returns the module. def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = { moduleClass.sourceModule = m m setModuleClass moduleClass m } - + /** Create a new free variable. Its owner is NoSymbol. */ def newFreeVar(name: TermName, tpe: Type, value: Any, newFlags: Long = 0L): FreeVar = @@ -67,7 +67,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def selfType: Type = typeOfThis def typeSignature: Type = info def typeSignatureIn(site: Type): Type = site memberInfo this - + def asType: Type = tpe def asTypeIn(site: Type): Type = site.memberType(this) def asTypeConstructor: Type = typeConstructor @@ -89,19 +89,19 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[this] var _rawowner = initOwner // Syncnote: need not be protected, as only assignment happens in owner_=, which is not exposed to api private[this] var _rawname = initName private[this] var _rawflags = 0L - + def rawowner = _rawowner def rawname = _rawname def rawflags = _rawflags - + protected def rawflags_=(x: FlagsType) { _rawflags = x } - + private var rawpos = initPos - + val id = nextId() // identity displayed when -uniqid private[this] var _validTo: Period = NoPeriod - + def validTo = _validTo def validTo_=(x: Period) { _validTo = x} @@ -179,10 +179,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def newTermSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): TermSymbol = new TermSymbol(this, pos, name) initFlags newFlags - + def newAbstractTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AbstractTypeSymbol = new AbstractTypeSymbol(this, pos, name) initFlags newFlags - + def newAliasTypeSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): AliasTypeSymbol = new AliasTypeSymbol(this, pos, name) initFlags newFlags @@ -194,10 +194,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => def newClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = new ClassSymbol(this, pos, name) initFlags newFlags - + def newModuleClassSymbol(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleClassSymbol = new ModuleClassSymbol(this, pos, name) initFlags newFlags - + /** Derive whether it is an abstract type from the flags; after creation * the DEFERRED flag will be ignored. */ @@ -206,7 +206,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => newAliasTypeSymbol(name, pos, newFlags) else newAbstractTypeSymbol(name, pos, newFlags) - + def newTypeSkolemSymbol(name: TypeName, origin: AnyRef, pos: Position = NoPosition, newFlags: Long = 0L): TypeSkolem = if ((newFlags & DEFERRED) == 0L) new TypeSkolem(this, pos, name, origin) initFlags newFlags @@ -243,7 +243,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ final def newAliasType(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): Symbol = newAliasTypeSymbol(name, pos, newFlags) - + /** Symbol of an abstract type type T >: ... <: ... */ final def newAbstractType(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): Symbol = @@ -261,7 +261,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def freshName() = { cnt += 1; nme.syntheticParamName(cnt) } mmap(argtypess)(tp => newValueParameter(freshName(), focusPos(owner.pos), SYNTHETIC) setInfo tp) } - + def newSyntheticTypeParam(): Symbol = newSyntheticTypeParam("T0", 0L) def newSyntheticTypeParam(name: String, newFlags: Long): Symbol = newTypeParameter(newTypeName(name), NoPosition, newFlags) setInfo TypeBounds.empty def newSyntheticTypeParams(num: Int): List[Symbol] = (0 until num).toList map (n => newSyntheticTypeParam("T" + n, 0L)) @@ -302,7 +302,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def newClass(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L) = newClassSymbol(name, pos, newFlags) - + /** A new class with its info set to a ClassInfoType with given scope and parents. */ def newClassWithInfo(name: TypeName, parents: List[Type], scope: Scope, pos: Position = NoPosition, newFlags: Long = 0L) = { val clazz = newClass(name, pos, newFlags) @@ -354,9 +354,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => def newAliasType(pos: Position, name: TypeName): Symbol = newAliasType(name, pos) @deprecated("Use the other signature", "2.10.0") def newAbstractType(pos: Position, name: TypeName): Symbol = newAbstractType(name, pos) - @deprecated("Use the other signature", "2.10.0") + @deprecated("Use the other signature", "2.10.0") def newExistential(pos: Position, name: TypeName): Symbol = newExistential(name, pos) - @deprecated("Use the other signature", "2.10.0") + @deprecated("Use the other signature", "2.10.0") def newMethod(pos: Position, name: TermName): MethodSymbol = newMethod(name, pos) // ----- locking and unlocking ------------------------------------------------------ @@ -499,11 +499,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => // class C extends D( { class E { ... } ... } ). Here, E is a class local to a constructor final def isClassLocalToConstructor = isClass && hasFlag(INCONSTRUCTOR) - final def isInlineClass = + final def isInlineClass = isClass && info.parents.headOption.getOrElse(AnyClass.tpe).typeSymbol == AnyValClass && !isPrimitiveValueClass - - final def isMethodWithExtension = + + final def isMethodWithExtension = isMethod && owner.isInlineClass && !isParamAccessor && !isConstructor final def isAnonymousClass = isClass && (name containsName tpnme.ANON_CLASS_NAME) @@ -845,7 +845,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => private def addModuleSuffix(n: Name): Name = if (needsModuleSuffix) n append nme.MODULE_SUFFIX_STRING else n - + def moduleSuffix: String = ( if (needsModuleSuffix) nme.MODULE_SUFFIX_STRING else "" @@ -853,7 +853,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Whether this symbol needs nme.MODULE_SUFFIX_STRING (aka $) appended on the java platform. */ def needsModuleSuffix = ( - hasModuleFlag + hasModuleFlag && !isMethod && !isImplClass && !isJavaDefined @@ -880,7 +880,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (owner.isEffectiveRoot) name else effectiveOwner.enclClass.fullNameAsName(separator) append separator append name ) - + def fullNameAsName(separator: Char): Name = nme.dropLocalSuffix(fullNameInternal(separator)) /** The encoded full path name of this symbol, where outer names and inner names @@ -1032,7 +1032,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Substitute second list of symbols for first in current info. */ def substInfo(syms0: List[Symbol], syms1: List[Symbol]) = modifyInfo(_.substSym(syms0, syms1)) def setInfoOwnerAdjusted(info: Type): this.type = setInfo(info atOwner this) - + /** Set the info and enter this symbol into the owner's scope. */ def setInfoAndEnter(info: Type): this.type = { setInfo(info) @@ -1341,7 +1341,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ final def isNestedIn(that: Symbol): Boolean = owner == that || owner != NoSymbol && (owner isNestedIn that) - + /** Is this class symbol a subclass of that symbol, * and is this class symbol also different from Null or Nothing? */ def isNonBottomSubClass(that: Symbol): Boolean = false @@ -1818,7 +1818,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => base.info.decl(sname) filter (_.hasAccessorFlag) } - /** The case module corresponding to this case class + /** Return the accessor method of the first parameter of this class. + * or NoSymbol if it does not exist. + */ + def firstParamAccessor: Symbol = NoSymbol + + /** The case module corresponding to this case class * @pre case class is a member of some other class or package */ final def caseModule: Symbol = { @@ -1859,7 +1864,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Remove any access boundary and clear flags PROTECTED | PRIVATE. */ def makePublic = this setPrivateWithin NoSymbol resetFlag AccessFlags - + /** The first parameter to the first argument list of this method, * or NoSymbol if inapplicable. */ @@ -2141,7 +2146,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def referenced: Symbol = _referenced def referenced_=(x: Symbol) { _referenced = x } - + def existentialBound = singletonBounds(this.tpe) def cloneSymbolImpl(owner: Symbol, newFlags: Long): Symbol = @@ -2235,7 +2240,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (!isMethod && needsFlatClasses) { if (flatname eq null) flatname = nme.flattenedName(rawowner.name, rawname) - + flatname } else rawname.toTermName @@ -2271,7 +2276,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => res } } - + class AliasTypeSymbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) { // Temporary programmatic help tracking down who might do such a thing @@ -2286,13 +2291,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def cloneSymbolImpl(owner: Symbol, newFlags: Long): AliasTypeSymbol = owner.newAliasTypeSymbol(name, pos, newFlags) } - + class AbstractTypeSymbol(initOwner: Symbol, initPos: Position, initName: TypeName) extends TypeSymbol(initOwner, initPos, initName) with AbstractTypeMixin { override def cloneSymbolImpl(owner: Symbol, newFlags: Long): AbstractTypeSymbol = owner.newAbstractTypeSymbol(name, pos, newFlags) } - + /** Might be mixed into TypeSymbol or TypeSkolem. */ trait AbstractTypeMixin extends TypeSymbol { @@ -2490,7 +2495,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final override def isNonClassType = false final override def isAbstractType = false final override def isAliasType = false - + override def existentialBound = GenPolyType(this.typeParams, TypeBounds.upper(this.classBound)) override def sourceFile = @@ -2518,19 +2523,19 @@ trait Symbols extends api.Symbols { self: SymbolTable => } thisTypeCache } - + override def owner: Symbol = if (needsFlatClasses) rawowner.owner else rawowner override def name: TypeName = ( if (needsFlatClasses) { if (flatname eq null) flatname = nme.flattenedName(rawowner.name, rawname).toTypeName - + flatname } else rawname.toTypeName ) - + /** A symbol carrying the self type of the class as its type */ override def thisSym: Symbol = thissym @@ -2566,6 +2571,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def sourceModule = if (isModuleClass) companionModule else NoSymbol + override def firstParamAccessor = + info.decls.find(m => (m hasFlag PARAMACCESSOR) && m.isMethod) getOrElse NoSymbol + + private[this] var childSet: Set[Symbol] = Set() override def children = childSet override def addChild(sym: Symbol) { childSet = childSet + sym } @@ -2713,7 +2722,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => val syms1 = cloneSymbolsAtOwner(syms, owner) creator(syms1, tpe.substSym(syms, syms1)) } - + /** A deep map on a symbol's paramss. */ def mapParamss[T](sym: Symbol)(f: Symbol => T): List[List[T]] = mmap(sym.info.paramss)(f) diff --git a/src/compiler/scala/reflect/internal/transform/Erasure.scala b/src/compiler/scala/reflect/internal/transform/Erasure.scala index f0981d7141..f8dfd66fbe 100644 --- a/src/compiler/scala/reflect/internal/transform/Erasure.scala +++ b/src/compiler/scala/reflect/internal/transform/Erasure.scala @@ -65,19 +65,16 @@ trait Erasure { if (cls.owner.isClass) cls.owner.tpe else pre // why not cls.isNestedClass? } - protected def valueClassErasure(clazz: Symbol): Type = + def valueClassErasure(clazz: Symbol): Type = clazz.primaryConstructor.info.params.head.tpe - + protected def eraseInlineClassRef(clazz: Symbol): Type = scalaErasure(valueClassErasure(clazz)) - - protected def underlyingParamAccessor(clazz: Symbol) = - clazz.info.decls.find(_ hasFlag PARAMACCESSOR).get - + abstract class ErasureMap extends TypeMap { def mergeParents(parents: List[Type]): Type - def eraseNormalClassRef(pre: Type, clazz: Symbol): Type = + def eraseNormalClassRef(pre: Type, clazz: Symbol): Type = typeRef(apply(rebindInnerClass(pre, clazz)), clazz, List()) // #2585 def apply(tp: Type): Type = { @@ -95,7 +92,7 @@ trait Erasure { else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass) else if (sym.isRefinementClass) apply(mergeParents(tp.parents)) else if (sym.isInlineClass) eraseInlineClassRef(sym) - else if (sym.isClass) eraseNormalClassRef(pre, sym) + else if (sym.isClass) eraseNormalClassRef(pre, sym) else apply(sym.info) // alias type or abstract type case PolyType(tparams, restpe) => apply(restpe) @@ -162,13 +159,11 @@ trait Erasure { log("Identified divergence between java/scala erasure:\n scala: " + old + "\n java: " + res) } res - } else if (sym.isMethodWithExtension || sym.isConstructor && sym.owner.isInlineClass) + } else if (sym.isTerm && sym.owner.isInlineClass) scalaErasureAvoiding(sym.owner, tp) - else if (sym.isValue && sym.owner.isMethodWithExtension) - scala.tools.nsc.util.trace("avoid unboxed: "+sym+"/"+sym.owner.owner+"/"+tp) { - scalaErasureAvoiding(sym.owner.owner, tp) - } - else + else if (sym.isValue && sym.owner.isMethodWithExtension) + scalaErasureAvoiding(sym.owner.owner, tp) + else scalaErasure(tp) } @@ -191,7 +186,7 @@ trait Erasure { def mergeParents(parents: List[Type]): Type = intersectionDominator(parents) } - + def scalaErasureAvoiding(clazz: Symbol, tpe: Type): Type = { tpe match { case PolyType(tparams, restpe) => diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 281a2eb49b..3f6a0f8f73 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -154,7 +154,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Register top level class (called on entering the class) */ def registerTopLevelSym(sym: Symbol) {} - + // ------------------ Reporting ------------------------------------- // not deprecated yet, but a method called "error" imported into @@ -500,6 +500,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb val runsRightAfter = Some("explicitouter") } with Erasure + // phaseName = "posterasure" + object postErasure extends { + val global: Global.this.type = Global.this + val runsAfter = List("erasure") + val runsRightAfter = Some("erasure") + } with PostErasure + // phaseName = "lazyvals" object lazyVals extends { final val FLAGS_PER_WORD = 32 @@ -659,6 +666,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb specializeTypes -> "@specialized-driven class and method specialization", explicitOuter -> "this refs to outer pointers, translate patterns", erasure -> "erase types, add interfaces for traits", + postErasure -> "clean up erased inline classes", lazyVals -> "allocate bitmaps, translate lazy vals into lazified defs", lambdaLift -> "move nested functions to top level", constructors -> "move field definitions into constructors", @@ -703,18 +711,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb private lazy val unitTimings = mutable.HashMap[CompilationUnit, Long]() withDefaultValue 0L // tracking time spent per unit private def unitTimingsFormatted(): String = { def toMillis(nanos: Long) = "%.3f" format nanos / 1000000d - + val formatter = new util.TableDef[(String, String)] { >> ("ms" -> (_._1)) >+ " " << ("path" -> (_._2)) } "" + ( - new formatter.Table(unitTimings.toList sortBy (-_._2) map { + new formatter.Table(unitTimings.toList sortBy (-_._2) map { case (unit, nanos) => (toMillis(nanos), unit.source.path) }) ) } - + protected def addToPhasesSet(sub: SubComponent, descr: String) { phasesSet += sub phasesDescMap(sub) = descr @@ -861,7 +869,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb /** Counts for certain classes of warnings during this run. */ var deprecationWarnings: List[(Position, String)] = Nil var uncheckedWarnings: List[(Position, String)] = Nil - + /** A flag whether macro expansions failed */ var macroExpansionFailed = false @@ -1008,7 +1016,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb } def cancel() { reporter.cancelled = true } - + private def currentProgress = (phasec * size) + unitc private def totalProgress = (phaseDescriptors.size - 1) * size // -1: drops terminal phase private def refreshProgress() = if (size > 0) progress(currentProgress, totalProgress) @@ -1175,12 +1183,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb */ def compileUnits(units: List[CompilationUnit], fromPhase: Phase) { try compileUnitsInternal(units, fromPhase) - catch { case ex => + catch { case ex => globalError(supplementErrorMessage("uncaught exception during compilation: " + ex.getClass.getName)) throw ex } } - + private def compileUnitsInternal(units: List[CompilationUnit], fromPhase: Phase) { units foreach addUnit if (opt.profileAll) { diff --git a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala index 81a6659b3c..9f361e5bcc 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala @@ -42,11 +42,11 @@ abstract class TreeInfo extends reflect.internal.TreeInfo { case ClassDef(_, `name`, _, _) :: Nil => true case _ => super.firstDefinesClassOrObject(trees, name) } - - def isInterface(mods: HasFlags, body: List[Tree]) = + + def isInterface(mods: HasFlags, body: List[Tree]) = mods.hasTraitFlag && (body forall isInterfaceMember) - def isAllowedInAnyTrait(stat: Tree): Boolean = stat match { + def isAllowedInUniversalTrait(stat: Tree): Boolean = stat match { case _: ValDef => false case Import(_, _) | EmptyTree => true case _: DefTree => true diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index fa64fbf48b..debf2e4b97 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -336,7 +336,7 @@ abstract class Erasure extends AddInterfaces class UnknownSig extends Exception override def eraseInlineClassRef(clazz: Symbol): Type = ErasedInlineType(clazz) - + /** The symbol's erased info. This is the type's erasure, except for the following symbols: * @@ -372,29 +372,47 @@ abstract class Erasure extends AddInterfaces override def newTyper(context: Context) = new Eraser(context) - /** An extractor object for boxed expressions + private def safeToRemoveUnbox(cls: Symbol): Boolean = + (cls == definitions.NullClass) || isBoxedValueClass(cls) + + /** An extractor object for unboxed expressions (maybe subsumed by posterasure?) */ + object Unboxed { + def unapply(tree: Tree): Option[Tree] = tree match { + case Apply(fn, List(arg)) if isUnbox(fn.symbol) && safeToRemoveUnbox(arg.tpe.typeSymbol) => + Some(arg) + case Apply( + TypeApply( + cast @ Select( + Apply( + sel @ Select(arg, acc), + List()), + asinstanceof), + List(tpt)), + List()) + if cast.symbol == Object_asInstanceOf && + tpt.tpe.typeSymbol.isInlineClass && + sel.symbol == tpt.tpe.typeSymbol.firstParamAccessor => + Some(arg) + case _ => + None + } + } + + /** An extractor object for boxed expressions (maybe subsumed by posterasure?) */ object Boxed { def unapply(tree: Tree): Option[Tree] = tree match { + case Apply(Select(New(tpt), nme.CONSTRUCTOR), List(arg)) if (tpt.tpe.typeSymbol.isInlineClass) => + Some(arg) case LabelDef(name, params, Boxed(rhs)) => Some(treeCopy.LabelDef(tree, name, params, rhs) setType rhs.tpe) - case Select(_, _) if tree.symbol == BoxedUnit_UNIT => - Some(Literal(Constant()) setPos tree.pos setType UnitClass.tpe) - case Block(List(unboxed), ret @ Select(_, _)) if ret.symbol == BoxedUnit_UNIT => - Some(if (unboxed.tpe.typeSymbol == UnitClass) tree - else Block(List(unboxed), Literal(Constant()) setPos tree.pos setType UnitClass.tpe)) - case Apply(fn, List(unboxed)) if isBox(fn.symbol) => - Some(unboxed) case _ => None } } - */ /** The modifier typer which retypes with erased types. */ class Eraser(_context: Context) extends Typer(_context) { - private def safeToRemoveUnbox(cls: Symbol): Boolean = - (cls == definitions.NullClass) || isBoxedValueClass(cls) - + private def isUnboxedType(tpe: Type) = tpe match { case ErasedInlineType(_) => true case _ => isPrimitiveValueClass(tpe.typeSymbol) @@ -403,24 +421,33 @@ abstract class Erasure extends AddInterfaces private def isUnboxedValueMember(sym: Symbol) = sym != NoSymbol && isPrimitiveValueClass(sym.owner) + private def box(tree: Tree, target: => String): Tree = { + val result = box1(tree) + log("boxing "+tree+":"+tree.tpe+" to "+target+" = "+result+":"+result.tpe) + result + } + /** Box `tree` of unboxed type */ - private def box(tree: Tree): Tree = tree match { + private def box1(tree: Tree): Tree = tree match { case LabelDef(name, params, rhs) => - val rhs1 = box(rhs) + val rhs1 = box1(rhs) treeCopy.LabelDef(tree, name, params, rhs1) setType rhs1.tpe case _ => val tree1 = tree.tpe match { case ErasedInlineType(clazz) => - util.trace("converting "+tree.tpe+" to "+valueClassErasure(clazz)+":")( - New(clazz, cast(tree, valueClassErasure(clazz))) - ) + tree match { + case Unboxed(arg) if arg.tpe.typeSymbol == clazz => + log("shortcircuiting unbox -> box "+arg); arg + case _ => + New(clazz, cast(tree, valueClassErasure(clazz))) + } case _ => tree.tpe.typeSymbol match { case UnitClass => if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT) else BLOCK(tree, REF(BoxedUnit_UNIT)) case NothingClass => tree // a non-terminating expression doesn't need boxing - case x => + case x => assert(x != ArrayClass) tree match { /** Can't always remove a Box(Unbox(x)) combination because the process of boxing x @@ -440,13 +467,19 @@ abstract class Erasure extends AddInterfaces typedPos(tree.pos)(tree1) } + private def unbox(tree: Tree, pt: Type): Tree = { + val result = unbox1(tree, pt) + log("unboxing "+tree+":"+tree.tpe+" to "+pt+" = "+result+":"+result.tpe) + result + } + /** Unbox `tree` of boxed type to expected type `pt`. * * @param tree the given tree * @param pt the expected type. * @return the unboxed tree */ - private def unbox(tree: Tree, pt: Type): Tree = tree match { + private def unbox1(tree: Tree, pt: Type): Tree = tree match { /* case Boxed(unboxed) => println("unbox shorten: "+tree) // this never seems to kick in during build and test; therefore disabled. @@ -458,8 +491,15 @@ abstract class Erasure extends AddInterfaces case _ => val tree1 = pt match { case ErasedInlineType(clazz) => - val tree0 = adaptToType(tree, valueClassErasure(clazz)) - cast(Apply(Select(tree0, underlyingParamAccessor(clazz)), List()), pt) + tree match { + case Boxed(arg) if arg.tpe.isInstanceOf[ErasedInlineType] => + log("shortcircuiting box -> unbox "+arg) + arg + case _ => + log("not boxed: "+tree) + val tree0 = adaptToType(tree, clazz.tpe) + cast(Apply(Select(tree0, clazz.firstParamAccessor), List()), pt) + } case _ => pt.typeSymbol match { case UnitClass => @@ -498,9 +538,7 @@ abstract class Erasure extends AddInterfaces if (tree.tpe <:< pt) tree else if (isUnboxedType(tree.tpe) && !isUnboxedType(pt)) { - val tree1 = util.trace("boxing "+tree.tpe+" to "+pt+" = ")(box(tree)) - println(tree1.tpe) - adaptToType(tree1, pt) + adaptToType(box(tree, pt.toString), pt) } else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.params.isEmpty) { assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt) adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) @@ -512,10 +550,6 @@ abstract class Erasure extends AddInterfaces cast(tree, pt) } - // @PP 1/25/2011: This is less inaccurate than it was (I removed - // BoxedAnyArray, asInstanceOf$erased, and other long ago eliminated symbols) - // but I do not think it yet describes the code beneath it. - /** Replace member references as follows: * * - `x == y` for == in class Any becomes `x equals y` with equals in class Object. @@ -532,7 +566,8 @@ abstract class Erasure extends AddInterfaces private def adaptMember(tree: Tree): Tree = { //Console.println("adaptMember: " + tree); tree match { - case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) if tree.symbol == Any_asInstanceOf => + case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) + if tree.symbol == Any_asInstanceOf || tree.symbol == Object_asInstanceOf => val qual1 = typedQualifier(qual, NOmode, ObjectClass.tpe) // need to have an expected type, see #3037 val qualClass = qual1.tpe.typeSymbol /* @@ -545,10 +580,11 @@ abstract class Erasure extends AddInterfaces */ if (isUnboxedType(targ.tpe)) unbox(qual1, targ.tpe) else tree - case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) if tree.symbol == Any_isInstanceOf => + case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) + if tree.symbol == Any_isInstanceOf || tree.symbol == Object_asInstanceOf => targ.tpe match { case ErasedInlineType(clazz) => targ.setType(scalaErasure(clazz.tpe)) - case _ => + case _ => } tree case Select(qual, name) => @@ -565,13 +601,12 @@ abstract class Erasure extends AddInterfaces adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, name)))) else { var qual1 = typedQualifier(qual) - if ((isPrimitiveValueClass(qual1.tpe.typeSymbol) && !isUnboxedValueMember(tree.symbol))) { - println("boxing "+qual1.tpe+" to member "+tree.symbol) - qual1 = box(qual1) - } else if (!isPrimitiveValueClass(qual1.tpe.typeSymbol) && isUnboxedValueMember(tree.symbol)) + if ((isUnboxedType(qual1.tpe) && !isUnboxedValueMember(tree.symbol))) + qual1 = box(qual1, "owner "+tree.symbol.owner) + else if (!isUnboxedType(qual1.tpe) && isUnboxedValueMember(tree.symbol)) qual1 = unbox(qual1, tree.symbol.owner.tpe) - if (isPrimitiveValueClass(tree.symbol.owner) && !isPrimitiveValueClass(qual1.tpe.typeSymbol)) + if (isUnboxedValueMember(tree.symbol) && !isUnboxedType(qual1.tpe)) tree.symbol = NoSymbol else if (qual1.tpe.isInstanceOf[MethodType] && qual1.tpe.params.isEmpty) { assert(qual1.symbol.isStable, qual1.symbol); @@ -998,7 +1033,7 @@ abstract class Erasure extends AddInterfaces } else { tree } - + case Select(qual, name) => val owner = tree.symbol.owner // println("preXform: "+ (tree, tree.symbol, tree.symbol.owner, tree.symbol.owner.isRefinementClass)) @@ -1065,7 +1100,7 @@ abstract class Erasure extends AddInterfaces */ override def transform(tree: Tree): Tree = { val tree1 = preTransformer.transform(tree) - println("tree after pretransform: "+tree1) + log("tree after pretransform: "+tree1) atPhase(phase.next) { val tree2 = mixinTransformer.transform(tree1) debuglog("tree after addinterfaces: \n" + tree2) diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 114ec721d8..e5f2d49d52 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -65,7 +65,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { matching.head } - private def normalize(stpe: Type, clazz: Symbol): Type = stpe match { + private def normalize(stpe: Type, clazz: Symbol): Type = stpe match { case PolyType(tparams, restpe) => GenPolyType(tparams dropRight clazz.typeParams.length, normalize(restpe, clazz)) case MethodType(tparams, restpe) => @@ -123,7 +123,6 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { def thisParamRef = gen.mkAttributedIdent(extensionMeth.info.params.head setPos extensionMeth.pos) val GenPolyType(extensionTpeParams, extensionMono) = extensionMeth.info val origTpeParams = origMeth.typeParams ::: currentOwner.typeParams - println("expanding "+tree+"/"+allParams(extensionMono)+"/"+extensionMeth.info) val extensionBody = rhs .substTreeSyms(origTpeParams, extensionTpeParams) .substTreeSyms(vparamss.flatten map (_.symbol), allParams(extensionMono).tail) diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 3ee5bf601d..e0b4072fa1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -39,6 +39,7 @@ trait SyntheticMethods extends ast.TreeDSL { /** Add the synthetic methods to case classes. */ def addSyntheticMethods(templ: Template, clazz0: Symbol, context: Context): Template = { + if (phase.erasedTypes) return templ @@ -47,8 +48,8 @@ trait SyntheticMethods extends ast.TreeDSL { newTyper( if (reporter.hasErrors) context makeSilent false else context ) ) import synthesizer._ - - if (clazz0 isSubClass AnyValClass) return { + + if (clazz0 == AnyValClass || isPrimitiveValueClass(clazz0)) return { if (clazz0.info member nme.getClass_ isDeferred) { val getClassMethod = createMethod(nme.getClass_, getClassReturnType(clazz.tpe)) { sym => // XXX dummy implementation for now @@ -96,8 +97,8 @@ trait SyntheticMethods extends ast.TreeDSL { def hasOverridingImplementation(meth: Symbol) = { val sym = clazz.info nonPrivateMember meth.name - sym.alternatives filterNot (_ eq meth) exists { m0 => - !m0.isDeferred && !m0.isSynthetic && (typeInClazz(m0) matches typeInClazz(meth)) + sym.alternatives exists { m0 => + (m0 ne meth) && !m0.isDeferred && !m0.isSynthetic && (m0.owner != AnyValClass) && (typeInClazz(m0) matches typeInClazz(meth)) } } def readConstantValue[T](name: String, default: T = null.asInstanceOf[T]): T = { @@ -122,13 +123,49 @@ trait SyntheticMethods extends ast.TreeDSL { // def productElementNameMethod = perElementMethod(nme.productElementName, StringClass.tpe)(x => LIT(x.name.toString)) + var syntheticCanEqual = false + /** The canEqual method for case classes. * def canEqual(that: Any) = that.isInstanceOf[This] */ - def canEqualMethod: Tree = ( - createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m => + def canEqualMethod: Tree = { + syntheticCanEqual = true + createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m => Ident(m.firstParam) IS_OBJ typeCaseType(clazz)) - ) + } + + /** (that.isInstanceOf[this.C]) + * where that is the given methods first parameter. + */ + def thatTest(eqmeth: Symbol): Tree = + gen.mkIsInstanceOf(Ident(eqmeth.firstParam), typeCaseType(clazz), true, false) + + /** (that.asInstanceOf[this.C]) + * where that is the given methods first parameter. + */ + def thatCast(eqmeth: Symbol): Tree = + gen.mkCast(Ident(eqmeth.firstParam), clazz.tpe) + + /** The equality method core for case classes and inline clases. + * 1+ args: + * (that.isInstanceOf[this.C]) && { + * val x$1 = that.asInstanceOf[this.C] + * (this.arg_1 == x$1.arg_1) && (this.arg_2 == x$1.arg_2) && ... && (x$1 canEqual this) + * } + * Drop canBuildFrom part if class is final and canBuildFrom is synthesized + */ + def equalsCore(eqmeth: Symbol, accessors: List[Symbol]) = { + val otherName = context.unit.freshTermName(clazz.name + "$") + val otherSym = eqmeth.newValue(otherName, eqmeth.pos, SYNTHETIC) setInfo clazz.tpe + val pairwise = accessors map (acc => fn(Select(This(clazz), acc), acc.tpe member nme.EQ, Select(Ident(otherSym), acc))) + val canEq = gen.mkMethodCall(otherSym, nme.canEqual_, Nil, List(This(clazz))) + val tests = if (clazz.isInlineClass || clazz.isFinal && syntheticCanEqual) pairwise else pairwise :+ canEq + + thatTest(eqmeth) AND Block( + ValDef(otherSym, thatCast(eqmeth)), + AND(tests: _*) + ) + } /** The equality method for case classes. * 0 args: @@ -141,34 +178,36 @@ trait SyntheticMethods extends ast.TreeDSL { * } * } */ - def equalsClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => - val arg0 = Ident(m.firstParam) - val thatTest = gen.mkIsInstanceOf(arg0, typeCaseType(clazz), true, false) - val thatCast = gen.mkCast(arg0, clazz.tpe) - - def argsBody: Tree = { - val otherName = context.unit.freshTermName(clazz.name + "$") - val otherSym = m.newValue(otherName, m.pos, SYNTHETIC) setInfo clazz.tpe - val pairwise = accessors map (acc => fn(Select(This(clazz), acc), acc.tpe member nme.EQ, Select(Ident(otherSym), acc))) - val canEq = gen.mkMethodCall(otherSym, nme.canEqual_, Nil, List(This(clazz))) - def block = Block(ValDef(otherSym, thatCast), AND(pairwise :+ canEq: _*)) - - (This(clazz) ANY_EQ arg0) OR { - thatTest AND Block( - ValDef(otherSym, thatCast), - AND(pairwise :+ canEq: _*) - ) - } - } + def equalsCaseClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => if (accessors.isEmpty) - thatTest AND ((thatCast DOT nme.canEqual_)(This(clazz))) + if (clazz.isFinal) thatTest(m) + else thatTest(m) AND ((thatCast(m) DOT nme.canEqual_)(This(clazz))) else - argsBody + (This(clazz) ANY_EQ Ident(m.firstParam)) OR equalsCore(m, accessors) + } + + /** The equality method for value classes + * def equals(that: Any) = (this.asInstanceOf[AnyRef]) eq that.asInstanceOf[AnyRef]) || { + * (that.isInstanceOf[this.C]) && { + * val x$1 = that.asInstanceOf[this.C] + * (this.underlying == that.underlying + */ + def equalsInlineClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => + equalsCore(m, List(clazz.firstParamAccessor)) + } + + /** The hashcode method for value classes + * def hashCode(): Int = this.underlying.hashCode + */ + def hashCodeInlineClassMethod: Tree = createMethod(nme.hashCode_, Nil, IntClass.tpe) { m => + Select( + Select(This(clazz), clazz.firstParamAccessor), + nme.hashCode_) } /** The _1, _2, etc. methods to implement ProductN. */ - def productNMethods = { + def productNMethods = { val accs = accessors.toIndexedSeq 1 to arity map (num => productProj(arity, num) -> (() => projectionMethod(accs(num - 1), num))) } @@ -190,7 +229,7 @@ trait SyntheticMethods extends ast.TreeDSL { def caseClassMethods = productMethods ++ productNMethods ++ Seq( Object_hashCode -> (() => forwardToRuntime(Object_hashCode)), Object_toString -> (() => forwardToRuntime(Object_toString)), - Object_equals -> (() => equalsClassMethod) + Object_equals -> (() => equalsCaseClassMethod) ) def caseObjectMethods = productMethods ++ Seq( @@ -200,6 +239,11 @@ trait SyntheticMethods extends ast.TreeDSL { // Object_equals -> (() => createMethod(Object_equals)(m => This(clazz) ANY_EQ Ident(m.firstParam))) ) + def inlineClassMethods = List( + Any_hashCode -> (() => hashCodeInlineClassMethod), + Any_equals -> (() => equalsInlineClassMethod) + ) + /** If you serialize a singleton and then deserialize it twice, * you will have two instances of your singleton unless you implement * readResolve. Here it is implemented for all objects which have @@ -214,10 +258,12 @@ trait SyntheticMethods extends ast.TreeDSL { def synthesize(): List[Tree] = { val methods = ( - if (!clazz.isCase) Nil + if (clazz.isInlineClass) inlineClassMethods + else if (!clazz.isCase) Nil else if (clazz.isModuleClass) caseObjectMethods else caseClassMethods ) + def impls = for ((m, impl) <- methods ; if !hasOverridingImplementation(m)) yield impl() def extras = ( if (needsReadResolve) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e6a3ddbe31..008a2e1764 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1193,6 +1193,27 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } + private def validateInlineClass(clazz: Symbol, body: List[Tree]) = { + if (clazz.isTrait) + unit.error(clazz.pos, "Only classes (not traits) are allowed to extend AnyVal") + if (!clazz.isStatic) + unit.error(clazz.pos, "Value class may not be a "+ + (if (clazz.owner.isTerm) "local class" else "member of another class")) + clazz.info.decls.toList.filter(acc => acc.isMethod && (acc hasFlag PARAMACCESSOR)) match { + case List(acc) => + def isUnderlyingAcc(sym: Symbol) = + sym == acc || acc.hasAccessorFlag && sym == acc.accessed + if (acc.accessBoundary(clazz) != RootClass) + unit.error(acc.pos, "Value class needs to have a publicly accessible val parameter") + else + for (stat <- body) + if (!treeInfo.isAllowedInUniversalTrait(stat) && !isUnderlyingAcc(stat.symbol)) + unit.error(stat.pos, "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") + } + } + def parentTypes(templ: Template): List[Tree] = if (templ.parents.isEmpty) List(TypeTree(AnyRefClass.tpe)) else try { @@ -1209,7 +1230,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { supertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus } } - if (supertpt.tpe.typeSymbol == AnyClass && firstParent.isTrait && firstParent != AnyValClass) + if (supertpt.tpe.typeSymbol == AnyClass && firstParent.isTrait) supertpt.tpe = AnyRefClass.tpe // Determine @@ -1300,7 +1321,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else xs ) } - + fixDuplicates(supertpt :: mixins) mapConserve (tpt => checkNoEscaping.privates(clazz, tpt)) } catch { @@ -1418,7 +1439,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val impl2 = finishMethodSynthesis(impl1, clazz, context) if (clazz.isTrait && clazz.info.parents.nonEmpty && clazz.info.firstParent.typeSymbol == AnyClass) for (stat <- impl2.body) - if (!treeInfo.isAllowedInAnyTrait(stat)) + if (!treeInfo.isAllowedInUniversalTrait(stat)) unit.error(stat.pos, "this statement is not allowed in trait extending from class Any: "+stat) if ((clazz != ClassfileAnnotationClass) && (clazz isNonBottomSubClass ClassfileAnnotationClass)) @@ -1536,6 +1557,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.owner.isPackageClass) unit.error(clazz.pos, "inner classes cannot be classfile annotations") + if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) @@ -1544,6 +1566,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { else templ.body flatMap rewrappingWrapperTrees(namer.finishGetterSetter(Typer.this, _)) val body1 = typedStats(body, templ.symbol) + + if (clazz.isInlineClass) + validateInlineClass(clazz, body1) + treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe } -- cgit v1.2.3