From e61eff23df977eeed19bfe253b01b69cce47dfa3 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 27 Jan 2012 16:30:01 -0800 Subject: Unsealed AnyVal. Hacks here and there to allow them to survive at least to erasure. Since nothing is done with them there yet, they inevitably crash and burn a little ways beyond that. --- test/files/pos/anyval-children.scala | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/files/pos/anyval-children.scala (limited to 'test/files/pos') diff --git a/test/files/pos/anyval-children.scala b/test/files/pos/anyval-children.scala new file mode 100644 index 0000000000..7a2eda8b3f --- /dev/null +++ b/test/files/pos/anyval-children.scala @@ -0,0 +1 @@ +class Bippy extends AnyVal \ No newline at end of file -- cgit v1.2.3 From 6eb066dade3f6541fb33a0b325c67ca98aa413ad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 30 Jan 2012 15:42:20 +0100 Subject: Resinstantiating anyval-children test. Getting he complete build to run. --- .../scala/reflect/internal/Definitions.scala | 14 +- src/compiler/scala/tools/nsc/Global.scala | 6 +- .../scala/tools/nsc/transform/ClassInliner.scala | 161 --------------------- .../scala/tools/nsc/transform/Erasure.scala | 4 +- .../tools/nsc/transform/ExtensionMethods.scala | 161 +++++++++++++++++++++ src/library/scala/AnyVal.scala | 3 +- test/disabled/pos/anyval-children.scala | 1 - test/files/pos/anyval-children.flags | 1 + test/files/pos/anyval-children.scala | 1 + 9 files changed, 182 insertions(+), 170 deletions(-) delete mode 100644 src/compiler/scala/tools/nsc/transform/ClassInliner.scala create mode 100644 src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala delete mode 100644 test/disabled/pos/anyval-children.scala create mode 100644 test/files/pos/anyval-children.flags create mode 100644 test/files/pos/anyval-children.scala (limited to 'test/files/pos') diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index e8fc1c9cc9..715a68cf55 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -659,6 +659,15 @@ trait Definitions extends reflect.api.StandardDefinitions { AnyClass, nme.isInstanceOf_, tparam => NullaryMethodType(booltype)) setFlag FINAL lazy val Any_asInstanceOf = newPolyMethod( AnyClass, nme.asInstanceOf_, tparam => NullaryMethodType(tparam.typeConstructor)) setFlag FINAL + + // AnyVal_getClass is defined here. Once we have a new strap, it could also be + // defined directly in the AnyVal trait. Right now this does not work, because + // strap complains about overriding a final getClass method in Any. + lazy val AnyVal_getClass = { + val m = AnyValClass.newMethod(nme.getClass_) + val tparam = m.newExistential(newTypeName("T")) setInfo TypeBounds(NothingClass.tpe, AnyValClass.tpe) + m setInfoAndEnter MethodType(List(), ExistentialType(List(tparam), appliedType(ClassClass.typeConstructor, List(tparam.tpe)))) + } // members of class java.lang.{ Object, String } lazy val Object_## = newMethod(ObjectClass, nme.HASHHASH, Nil, inttype, FINAL) @@ -859,7 +868,7 @@ trait Definitions extends reflect.api.StandardDefinitions { val msym = owner.info.decls enter owner.newMethod(name.encode) val tparam = newTypeParam(msym, 0) - msym setInfo GenPolyType(List(tparam), tcon(tparam)(msym)) + msym setInfo PolyType(List(tparam), tcon(tparam)(msym)) } private def newTypeParam(owner: Symbol, index: Int): Symbol = @@ -991,7 +1000,8 @@ trait Definitions extends reflect.api.StandardDefinitions { Object_synchronized, Object_isInstanceOf, Object_asInstanceOf, - String_+ + String_+, + AnyVal_getClass ) /** Removing the anyref parent they acquire from having a source file. diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 1092c7bce1..8026e92a21 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -436,11 +436,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb val global: Global.this.type = Global.this } with Analyzer - object classInlining extends { + object extensionMethods extends { val global: Global.this.type = Global.this val runsAfter = List("typer") val runsRightAfter = None - } with ClassInlining + } with ExtensionMethods // phaseName = "superaccessors" object superAccessors extends { @@ -656,7 +656,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb analyzer.packageObjects -> "load package objects", analyzer.typerFactory -> "the meat and potatoes: type the trees", superAccessors -> "add super accessors in traits and nested classes", - classInlining -> "add static methods to inline classes", + extensionMethods -> "add extension methods for inline classes", pickler -> "serialize symbol tables", refChecks -> "reference/override checking, translate nested objects", liftcode -> "reify trees", diff --git a/src/compiler/scala/tools/nsc/transform/ClassInliner.scala b/src/compiler/scala/tools/nsc/transform/ClassInliner.scala deleted file mode 100644 index c1104b025c..0000000000 --- a/src/compiler/scala/tools/nsc/transform/ClassInliner.scala +++ /dev/null @@ -1,161 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Gilles Dubochet - * @author Martin Odersky - */ - -package scala.tools.nsc -package transform - -import symtab._ -import Flags._ -import scala.collection.{ mutable, immutable } -import scala.collection.mutable -import scala.tools.nsc.util.FreshNameCreator -import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple } -import sun.tools.tree.OrExpression - -/** - * Perform Step 1 in the inline classes SIP - * - * @author Martin Odersky - * @version 2.10 - */ -abstract class ClassInlining extends Transform with TypingTransformers { - - import global._ // the global environment - import definitions._ // standard classes and methods - import typer.{ typed, atOwner } // methods to type trees - - /** the following two members override abstract members in Transform */ - val phaseName: String = "inlineclasses" - - def newTransformer(unit: CompilationUnit): Transformer = - new ClassInliner(unit) - - def hasUnboxedVersion(sym: Symbol) = - !sym.isParamAccessor && !sym.isConstructor - - /** Generate stream of possible names for the unboxed version of given instance method `imeth`. - * If the method is not overloaded, this stream consists of just "unboxed$imeth". - * If the method is overloaded, the stream has as first element "unboxedX$imeth", where X is the - * index of imeth in the sequence of overloaded alternatives with the same name. This choice will - * always be picked as the name of the generated unboxed method. - * After this first choice, all other possible indices in the range of 0 until the number - * of overloaded alternatives are returned. The secondary choices are used to find a matching method - * in `unboxedMethod` if the first name has the wrong type. We thereby gain a level of insensitivity - * of how overloaded types are ordered between phases and picklings. - */ - private def unboxedNames(imeth: Symbol): Stream[Name] = - imeth.owner.info.decl(imeth.name).tpe match { - case OverloadedType(_, alts) => - val index = alts indexOf imeth - assert(index >= 0, alts+" does not contain "+imeth) - def altName(index: Int) = newTermName("unboxed"+index+"$"+imeth.name) - altName(index) #:: ((0 until alts.length).toStream filter (index !=) map altName) - case tpe => - assert(tpe != NoType, imeth.name+" not found in "+imeth.owner+"'s decls: "+imeth.owner.info.decls) - Stream(newTermName("unboxed$"+imeth.name)) - } - - /** Return the unboxed method that corresponds to given instance method `meth`. - */ - def unboxedMethod(imeth: Symbol): Symbol = atPhase(currentRun.refchecksPhase) { - val companionInfo = imeth.owner.companionModule.info - val candidates = unboxedNames(imeth) map (companionInfo.decl(_)) - val matching = candidates filter (alt => normalize(alt.tpe, imeth.owner) matches imeth.tpe) - assert(matching.nonEmpty, "no unboxed method found for "+imeth+" among "+candidates+"/"+unboxedNames(imeth)) - matching.head - } - - 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) => - restpe - case _ => - stpe - } - - class ClassInliner(unit: CompilationUnit) extends TypingTransformer(unit) { - - private val unboxedDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() - - def unboxedMethInfo(unboxedMeth: Symbol, origInfo: Type, clazz: Symbol): Type = { - var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, unboxedMeth) - val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpe)) - val thisParam = unboxedMeth.newValueParameter(nme.SELF, unboxedMeth.pos) setInfo thisParamType - def transform(clonedType: Type): Type = clonedType match { - case MethodType(params, restpe) => - MethodType(List(thisParam), clonedType) - case NullaryMethodType(restpe) => - MethodType(List(thisParam), restpe) - } - val GenPolyType(tparams, restpe) = origInfo cloneInfo unboxedMeth - GenPolyType(tparams ::: newTypeParams, transform(restpe)) - } - - private def allParams(tpe: Type): List[Symbol] = tpe match { - case MethodType(params, res) => params ::: allParams(res) - case _ => List() - } - - override def transform(tree: Tree): Tree = { - tree match { - case Template(_, _, _) => - if (currentOwner.isInlineClass) { - unboxedDefs(currentOwner.companionModule) = new mutable.ListBuffer[Tree] - super.transform(tree) - } - else tree - case DefDef(mods, name, tparams, vparamss, tpt, rhs) - if currentOwner.isInlineClass && hasUnboxedVersion(tree.symbol) => - val companion = currentOwner.companionModule - val origMeth = tree.symbol - val unboxedName = unboxedNames(origMeth).head - val unboxedMeth = companion.moduleClass.newMethod(unboxedName, origMeth.pos, origMeth.flags & ~OVERRIDE | FINAL) - .setAnnotations(origMeth.annotations) - companion.info.decls.enter(unboxedMeth) - unboxedMeth.setInfo(unboxedMethInfo(unboxedMeth, origMeth.info, currentOwner)) - def thisParamRef = gen.mkAttributedIdent(unboxedMeth.info.params.head setPos unboxedMeth.pos) - val GenPolyType(unboxedTpeParams, unboxedMono) = unboxedMeth.info - val origTpeParams = origMeth.typeParams ::: currentOwner.typeParams - val unboxedBody = rhs - .substTreeSyms(origTpeParams, unboxedTpeParams) - .substTreeSyms(vparamss.flatten map (_.symbol), allParams(unboxedMono).tail) - .substTreeThis(currentOwner, thisParamRef) - .changeOwner((origMeth, unboxedMeth)) - unboxedDefs(companion) += atPos(tree.pos) { DefDef(unboxedMeth, unboxedBody) } - val unboxedCallPrefix = Apply( - gen.mkTypeApply(gen.mkAttributedRef(companion), unboxedMeth, origTpeParams map (_.tpe)), - List(This(currentOwner))) - val unboxedCall = atOwner(origMeth) { - localTyper.typed { - atPos(rhs.pos) { - (unboxedCallPrefix /: vparamss) { - case (fn, params) => Apply(fn, params map (param => Ident(param.symbol))) - } - } - } - } - treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, unboxedCall) - case _ => - super.transform(tree) - } - } - - override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = - super.transformStats(stats, exprOwner) map { - case stat @ ModuleDef(mods, name, tmpl @ Template(parents, self, body)) => - unboxedDefs.remove(stat.symbol) match { - case Some(buf) => - val unboxedDefs = buf.toList map { mdef => atOwner(stat.symbol) { localTyper.typed(mdef) }} - treeCopy.ModuleDef(stat, mods, name, treeCopy.Template(tmpl, parents, self, body ++ buf)) - case None => - stat - } - case stat => - stat - } - } -} diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 51941408a2..5b1555bfb7 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -986,8 +986,8 @@ abstract class Erasure extends AddInterfaces } } else if (fn.symbol.owner.isRefinementClass && !fn.symbol.isOverridingSymbol) { ApplyDynamic(qualifier, args) setSymbol fn.symbol setPos tree.pos - } else if (fn.symbol.owner.isInlineClass && classInlining.hasUnboxedVersion(fn.symbol)) { - Apply(gen.mkAttributedRef(classInlining.unboxedMethod(fn.symbol)), qualifier :: args) + } else if (fn.symbol.owner.isInlineClass && extensionMethods.hasExtension(fn.symbol)) { + Apply(gen.mkAttributedRef(extensionMethods.extensionMethod(fn.symbol)), qualifier :: args) } else { tree } diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala new file mode 100644 index 0000000000..5f62dfab39 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -0,0 +1,161 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Gilles Dubochet + * @author Martin Odersky + */ + +package scala.tools.nsc +package transform + +import symtab._ +import Flags._ +import scala.collection.{ mutable, immutable } +import scala.collection.mutable +import scala.tools.nsc.util.FreshNameCreator +import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple } +import sun.tools.tree.OrExpression + +/** + * Perform Step 1 in the inline classes SIP + * + * @author Martin Odersky + * @version 2.10 + */ +abstract class ExtensionMethods extends Transform with TypingTransformers { + + import global._ // the global environment + import definitions._ // standard classes and methods + import typer.{ typed, atOwner } // methods to type trees + + /** the following two members override abstract members in Transform */ + val phaseName: String = "extmethods" + + def newTransformer(unit: CompilationUnit): Transformer = + new Extender(unit) + + def hasExtension(sym: Symbol) = + !sym.isParamAccessor && !sym.isConstructor + + /** Generate stream of possible names for the extension version of given instance method `imeth`. + * If the method is not overloaded, this stream consists of just "extension$imeth". + * If the method is overloaded, the stream has as first element "extensionX$imeth", where X is the + * index of imeth in the sequence of overloaded alternatives with the same name. This choice will + * always be picked as the name of the generated extension method. + * After this first choice, all other possible indices in the range of 0 until the number + * of overloaded alternatives are returned. The secondary choices are used to find a matching method + * in `extensionMethod` if the first name has the wrong type. We thereby gain a level of insensitivity + * of how overloaded types are ordered between phases and picklings. + */ + private def extensionNames(imeth: Symbol): Stream[Name] = + imeth.owner.info.decl(imeth.name).tpe match { + case OverloadedType(_, alts) => + val index = alts indexOf imeth + assert(index >= 0, alts+" does not contain "+imeth) + def altName(index: Int) = newTermName("extension"+index+"$"+imeth.name) + altName(index) #:: ((0 until alts.length).toStream filter (index !=) map altName) + case tpe => + assert(tpe != NoType, imeth.name+" not found in "+imeth.owner+"'s decls: "+imeth.owner.info.decls) + Stream(newTermName("extension$"+imeth.name)) + } + + /** Return the extension method that corresponds to given instance method `meth`. + */ + def extensionMethod(imeth: Symbol): Symbol = atPhase(currentRun.refchecksPhase) { + val companionInfo = imeth.owner.companionModule.info + val candidates = extensionNames(imeth) map (companionInfo.decl(_)) + val matching = candidates filter (alt => normalize(alt.tpe, imeth.owner) matches imeth.tpe) + assert(matching.nonEmpty, "no extension method found for "+imeth+" among "+candidates+"/"+extensionNames(imeth)) + matching.head + } + + 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) => + restpe + case _ => + stpe + } + + class Extender(unit: CompilationUnit) extends TypingTransformer(unit) { + + private val extensionDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() + + def extensionMethInfo(extensionMeth: Symbol, origInfo: Type, clazz: Symbol): Type = { + var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) + val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpe)) + val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType + def transform(clonedType: Type): Type = clonedType match { + case MethodType(params, restpe) => + MethodType(List(thisParam), clonedType) + case NullaryMethodType(restpe) => + MethodType(List(thisParam), restpe) + } + val GenPolyType(tparams, restpe) = origInfo cloneInfo extensionMeth + GenPolyType(tparams ::: newTypeParams, transform(restpe)) + } + + private def allParams(tpe: Type): List[Symbol] = tpe match { + case MethodType(params, res) => params ::: allParams(res) + case _ => List() + } + + override def transform(tree: Tree): Tree = { + tree match { + case Template(_, _, _) => + if (currentOwner.isInlineClass) { + extensionDefs(currentOwner.companionModule) = new mutable.ListBuffer[Tree] + super.transform(tree) + } + else tree + case DefDef(mods, name, tparams, vparamss, tpt, rhs) + if currentOwner.isInlineClass && hasExtension(tree.symbol) => + val companion = currentOwner.companionModule + val origMeth = tree.symbol + val extensionName = extensionNames(origMeth).head + val extensionMeth = companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE | FINAL) + .setAnnotations(origMeth.annotations) + companion.info.decls.enter(extensionMeth) + extensionMeth.setInfo(extensionMethInfo(extensionMeth, origMeth.info, currentOwner)) + def thisParamRef = gen.mkAttributedIdent(extensionMeth.info.params.head setPos extensionMeth.pos) + val GenPolyType(extensionTpeParams, extensionMono) = extensionMeth.info + val origTpeParams = origMeth.typeParams ::: currentOwner.typeParams + val extensionBody = rhs + .substTreeSyms(origTpeParams, extensionTpeParams) + .substTreeSyms(vparamss.flatten map (_.symbol), allParams(extensionMono).tail) + .substTreeThis(currentOwner, thisParamRef) + .changeOwner((origMeth, extensionMeth)) + extensionDefs(companion) += atPos(tree.pos) { DefDef(extensionMeth, extensionBody) } + val extensionCallPrefix = Apply( + gen.mkTypeApply(gen.mkAttributedRef(companion), extensionMeth, origTpeParams map (_.tpe)), + List(This(currentOwner))) + val extensionCall = atOwner(origMeth) { + localTyper.typed { + atPos(rhs.pos) { + (extensionCallPrefix /: vparamss) { + case (fn, params) => Apply(fn, params map (param => Ident(param.symbol))) + } + } + } + } + treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, extensionCall) + case _ => + super.transform(tree) + } + } + + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = + super.transformStats(stats, exprOwner) map { + case stat @ ModuleDef(mods, name, tmpl @ Template(parents, self, body)) => + extensionDefs.remove(stat.symbol) match { + case Some(buf) => + val extensionDefs = buf.toList map { mdef => atOwner(stat.symbol) { localTyper.typed(mdef) }} + treeCopy.ModuleDef(stat, mods, name, treeCopy.Template(tmpl, parents, self, body ++ buf)) + case None => + stat + } + case stat => + stat + } + } +} diff --git a/src/library/scala/AnyVal.scala b/src/library/scala/AnyVal.scala index ed4c5f5948..fb36d61c57 100644 --- a/src/library/scala/AnyVal.scala +++ b/src/library/scala/AnyVal.scala @@ -26,6 +26,7 @@ package scala * The ''floating point types'' are [[scala.Float]] and [[scala.Double]]. */ trait AnyVal { -// disabled for now to make the standard build go through +// disabled for now to make the standard build go through. +// Once we have a new strap we can uncomment this and delete the AnyVal_getClass entry in Definitions. // def getClass(): Class[_ <: AnyVal] = ??? } diff --git a/test/disabled/pos/anyval-children.scala b/test/disabled/pos/anyval-children.scala deleted file mode 100644 index 7a2eda8b3f..0000000000 --- a/test/disabled/pos/anyval-children.scala +++ /dev/null @@ -1 +0,0 @@ -class Bippy extends AnyVal \ No newline at end of file diff --git a/test/files/pos/anyval-children.flags b/test/files/pos/anyval-children.flags new file mode 100644 index 0000000000..80fce051e6 --- /dev/null +++ b/test/files/pos/anyval-children.flags @@ -0,0 +1 @@ +-Ystop-after:erasure \ No newline at end of file diff --git a/test/files/pos/anyval-children.scala b/test/files/pos/anyval-children.scala new file mode 100644 index 0000000000..7a2eda8b3f --- /dev/null +++ b/test/files/pos/anyval-children.scala @@ -0,0 +1 @@ +class Bippy extends AnyVal \ No newline at end of file -- cgit v1.2.3 From a9eb9c5b69071a944d2a5225aa320babdf33ad42 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 30 Jan 2012 12:42:26 -0800 Subject: More work on inline classes. Fail compile if AnyVal is inherited by a trait, a non-@inline class, or a class with an AnyRef parent somewhere. Added tests. Added logging, like [log extmethods] Inline class class Bippy spawns extension method. Old: def getClass: Class[_ <: Bippy] New: final def extension$getClass($this: Bippy): Class[_ <: Bippy] Fixed what I hope was a bug in ExtensionMethods where the original method params were dropped. Since adding a NonNull parent was also inflicting an AnyRef on AnyVal subclasses, suppressed that for those. Had the bright idea that AnyVal could extend NotNull. It doesn't seem to accomplish much, but then, I don't think NotNull accomplishes much. Still, maybe it's time to restrict the ways one can use AnyVal so one can't do this: scala> var x: AnyVal = _ x: AnyVal = null --- .../scala/reflect/internal/Definitions.scala | 8 ++++++- .../scala/tools/nsc/ast/parser/Parsers.scala | 2 +- .../tools/nsc/transform/ExtensionMethods.scala | 13 ++++++++--- .../scala/tools/nsc/typechecker/Namers.scala | 6 +++-- .../scala/tools/nsc/typechecker/RefChecks.scala | 23 +++++++++++++++---- src/library/scala/AnyVal.scala | 2 +- src/library/scala/NotNull.scala | 2 -- test/files/neg/anyval-children-2.check | 4 ++++ test/files/neg/anyval-children-2.scala | 1 + test/files/neg/anyval-children.check | 26 ++++++++++++++++++++++ test/files/neg/anyval-children.scala | 14 ++++++++++++ test/files/pos/anyval-children.scala | 2 +- 12 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 test/files/neg/anyval-children-2.check create mode 100644 test/files/neg/anyval-children-2.scala create mode 100644 test/files/neg/anyval-children.check create mode 100644 test/files/neg/anyval-children.scala (limited to 'test/files/pos') diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index f8342444c8..ce0505bf44 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -910,6 +910,12 @@ trait Definitions extends reflect.api.StandardDefinitions { private lazy val scalaValueClassesSet = ScalaValueClasses.toSet private lazy val boxedValueClassesSet = boxedClass.values.toSet + BoxedUnitClass + /** Now that AnyVal is unsealing we need less ambiguous names + * for when we need to distinguish the Nine Original AnyVals + * from the heathen masses. + */ + def isPrimitiveValueClass(sym: Symbol) = scalaValueClassesSet(sym) + /** Is symbol a value class? */ def isValueClass(sym: Symbol) = scalaValueClassesSet(sym) def isNonUnitValueClass(sym: Symbol) = (sym != UnitClass) && isValueClass(sym) @@ -1029,7 +1035,7 @@ trait Definitions extends reflect.api.StandardDefinitions { /** Removing the anyref parent they acquire from having a source file. */ - setParents(AnyValClass, anyparam) + setParents(AnyValClass, List(NotNullClass.tpe, AnyClass.tpe)) ScalaValueClasses foreach { sym => setParents(sym, anyvalparam) } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index eb7c5a6699..3ee8b62bc6 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2757,7 +2757,7 @@ self => Template(List(scalaDot(tpnme.AnyVal)), self, body) } else if (parents0 exists isReferenceToAnyVal) { - // TODO - enforce @inline annotation, and no other parents + // @inline and other restrictions enforced in refchecks Template(parents0, self, body) } else if (name == tpnme.AnyVal) { diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 5f62dfab39..c308a3633e 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -84,10 +84,11 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { def extensionMethInfo(extensionMeth: Symbol, origInfo: Type, clazz: Symbol): Type = { var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpe)) - val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType + val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType def transform(clonedType: Type): Type = clonedType match { case MethodType(params, restpe) => - MethodType(List(thisParam), clonedType) + // I assume it was a bug that this was dropping params... + MethodType(thisParam :: params, clonedType) case NullaryMethodType(restpe) => MethodType(List(thisParam), restpe) } @@ -116,7 +117,13 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { val extensionMeth = companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE | FINAL) .setAnnotations(origMeth.annotations) companion.info.decls.enter(extensionMeth) - extensionMeth.setInfo(extensionMethInfo(extensionMeth, origMeth.info, currentOwner)) + val newInfo = extensionMethInfo(extensionMeth, origMeth.info, currentOwner) + extensionMeth setInfo newInfo + log("Inline class %s spawns extension method.\n Old: %s\n New: %s".format( + currentOwner, + origMeth.defString, + extensionMeth.defString)) // extensionMeth.defStringSeenAs(origInfo + def thisParamRef = gen.mkAttributedIdent(extensionMeth.info.params.head setPos extensionMeth.pos) val GenPolyType(extensionTpeParams, extensionMono) = extensionMeth.info val origTpeParams = origMeth.typeParams ::: currentOwner.typeParams diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index b9f3adec75..2f31347829 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1407,7 +1407,9 @@ trait Namers extends MethodSynthesis { } if (sym.isClass && sym.hasAnnotation(ScalaInlineClass) && !phase.erasedTypes) { - ensureParent(NotNullClass) + if (!sym.isSubClass(AnyValClass)) + ensureParent(NotNullClass) + sym setFlag FINAL } @@ -1437,7 +1439,7 @@ trait Namers extends MethodSynthesis { checkNoConflict(PRIVATE, PROTECTED) // checkNoConflict(PRIVATE, OVERRIDE) // this one leads to bad error messages like #4174, so catch in refchecks // checkNoConflict(PRIVATE, FINAL) // can't do this because FINAL also means compile-time constant - checkNoConflict(ABSTRACT, FINAL) + // checkNoConflict(ABSTRACT, FINAL) // this one gives a bad error for non-@inline classes which extend AnyVal // @PP: I added this as a sanity check because these flags are supposed to be // converted to ABSOVERRIDE before arriving here. checkNoConflict(ABSTRACT, OVERRIDE) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 4449116fd1..f7a6815905 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -683,16 +683,16 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R if (abstractErrors.nonEmpty) unit.error(clazz.pos, abstractErrorMessage) - } else if (clazz.isTrait) { - // prevent abstract methods in interfaces that override final members in Object; see #4431 - if (!(clazz isSubClass AnyValClass)) { + } + else if (clazz.isTrait && !(clazz isSubClass AnyValClass)) { + // For non-AnyVal classes, prevent abstract methods in interfaces that override + // final members in Object; see #4431 for (decl <- clazz.info.decls.iterator) { val overridden = decl.overriddenSymbol(ObjectClass) if (overridden.isFinal) unit.error(decl.pos, "trait cannot redefine final method from class AnyRef") } } - } /** Returns whether there is a symbol declared in class `inclazz` * (which must be different from `clazz`) whose name and type @@ -1527,6 +1527,19 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R ) case _ => () } + + // verify classes extending AnyVal meet the requirements + // (whatever those are to be, but at least: @inline annotation) + private def checkAnyValSubclass(clazz: Symbol) = { + if ((clazz isSubClass AnyValClass) && (clazz ne AnyValClass) && !isPrimitiveValueClass(clazz)) { + if (!clazz.hasAnnotation(ScalaInlineClass)) + unit.error(clazz.pos, "Only @inline classes are allowed to extend AnyVal") + if (clazz.isTrait) + unit.error(clazz.pos, "Only @inline classes (not traits) are allowed to extend AnyVal") + if (clazz.tpe <:< AnyRefClass.tpe) + unit.error(clazz.pos, "Classes which extend AnyVal may not have an ancestor which inherits AnyRef") + } + } override def transform(tree: Tree): Tree = { val savedLocalTyper = localTyper @@ -1562,6 +1575,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R checkOverloadedRestrictions(currentOwner) val bridges = addVarargBridges(currentOwner) checkAllOverrides(currentOwner) + checkAnyValSubclass(currentOwner) + if (bridges.nonEmpty) treeCopy.Template(tree, parents, self, body ::: bridges) else tree diff --git a/src/library/scala/AnyVal.scala b/src/library/scala/AnyVal.scala index fb36d61c57..ed32fb7302 100644 --- a/src/library/scala/AnyVal.scala +++ b/src/library/scala/AnyVal.scala @@ -25,7 +25,7 @@ package scala * The ''integer types'' include the subrange types as well as [[scala.Int]] and [[scala.Long]]. * The ''floating point types'' are [[scala.Float]] and [[scala.Double]]. */ -trait AnyVal { +trait AnyVal extends NotNull { // disabled for now to make the standard build go through. // Once we have a new strap we can uncomment this and delete the AnyVal_getClass entry in Definitions. // def getClass(): Class[_ <: AnyVal] = ??? diff --git a/src/library/scala/NotNull.scala b/src/library/scala/NotNull.scala index d47d47a83e..f90b95c789 100644 --- a/src/library/scala/NotNull.scala +++ b/src/library/scala/NotNull.scala @@ -6,8 +6,6 @@ ** |/ ** \* */ - - package scala /** diff --git a/test/files/neg/anyval-children-2.check b/test/files/neg/anyval-children-2.check new file mode 100644 index 0000000000..cb327faeeb --- /dev/null +++ b/test/files/neg/anyval-children-2.check @@ -0,0 +1,4 @@ +anyval-children-2.scala:1: error: Only @inline classes (not traits) are allowed to extend AnyVal +@inline trait NotOkDingus1 extends AnyVal // fail + ^ +one error found diff --git a/test/files/neg/anyval-children-2.scala b/test/files/neg/anyval-children-2.scala new file mode 100644 index 0000000000..4034eb22dd --- /dev/null +++ b/test/files/neg/anyval-children-2.scala @@ -0,0 +1 @@ +@inline trait NotOkDingus1 extends AnyVal // fail diff --git a/test/files/neg/anyval-children.check b/test/files/neg/anyval-children.check new file mode 100644 index 0000000000..cbb5a2b1d1 --- /dev/null +++ b/test/files/neg/anyval-children.check @@ -0,0 +1,26 @@ +anyval-children.scala:7: error: illegal inheritance; superclass Bippy + is not a subclass of the superclass Object + of the mixin trait ScalaObject +class NotOkBippy1 extends Bippy // fail + ^ +anyval-children.scala:9: error: illegal inheritance; superclass Bippy + is not a subclass of the superclass Object + of the mixin trait Immutable +class NotOkBippy2 extends Bippy with Immutable //fail + ^ +anyval-children.scala:9: error: illegal inheritance; superclass Bippy + is not a subclass of the superclass Object + of the mixin trait ScalaObject +class NotOkBippy2 extends Bippy with Immutable //fail + ^ +anyval-children.scala:11: error: illegal inheritance; superclass Bippy + is not a subclass of the superclass Object + of the mixin trait Immutable +@inline class NotOkBippy3 extends Bippy with Immutable //fail + ^ +anyval-children.scala:11: error: illegal inheritance; superclass Bippy + is not a subclass of the superclass Object + of the mixin trait ScalaObject +@inline class NotOkBippy3 extends Bippy with Immutable //fail + ^ +5 errors found diff --git a/test/files/neg/anyval-children.scala b/test/files/neg/anyval-children.scala new file mode 100644 index 0000000000..5a6109f786 --- /dev/null +++ b/test/files/neg/anyval-children.scala @@ -0,0 +1,14 @@ +class Bippy extends AnyVal // fail + +@inline class NotOkDingus2 extends Immutable with AnyVal // fail + +@inline object NotOkDingus3 extends AnyVal // fail + +class NotOkBippy1 extends Bippy // fail + +class NotOkBippy2 extends Bippy with Immutable //fail + +@inline class NotOkBippy3 extends Bippy with Immutable //fail + + +@inline class OkBippy extends AnyVal // ok diff --git a/test/files/pos/anyval-children.scala b/test/files/pos/anyval-children.scala index 7a2eda8b3f..4ef10a094f 100644 --- a/test/files/pos/anyval-children.scala +++ b/test/files/pos/anyval-children.scala @@ -1 +1 @@ -class Bippy extends AnyVal \ No newline at end of file +@inline class Bippy extends AnyVal \ No newline at end of file -- cgit v1.2.3 From 2b7fb460dd1e666ed37ccba49a82aab959413c22 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 4 Feb 2012 11:22:49 -0800 Subject: Updated checkfiles to subtract ScalaObject. --- test/files/buildmanager/t2556_3/t2556_3.check | 4 ++-- test/files/jvm/manifests.check | 2 +- test/files/neg/anyval-children.check | 12 +++--------- test/files/neg/t0764.check | 2 +- test/files/neg/t2641.check | 6 +----- test/files/neg/t3691.check | 4 ++-- test/files/neg/t464-neg.check | 4 ++-- test/files/neg/t4877.check | 2 +- test/files/neg/t5060.check | 4 ++-- test/files/neg/t664.check | 4 ++-- test/files/neg/variances.check | 2 +- test/files/pos/t1050.scala | 2 +- test/files/presentation/callcc-interpreter.check | 4 ++-- test/files/run/primitive-sigs-2.check | 2 +- test/files/run/repl-parens.check | 2 +- test/files/run/t4110.check | 4 ++-- test/files/run/t4172.check | 2 +- test/files/run/t4891.check | 1 - 18 files changed, 26 insertions(+), 37 deletions(-) (limited to 'test/files/pos') diff --git a/test/files/buildmanager/t2556_3/t2556_3.check b/test/files/buildmanager/t2556_3/t2556_3.check index bf26602494..37808d2b31 100644 --- a/test/files/buildmanager/t2556_3/t2556_3.check +++ b/test/files/buildmanager/t2556_3/t2556_3.check @@ -3,8 +3,8 @@ compiling Set(A.scala, B.scala, C.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(class A -> List(), class B -> List(Changed(Class(B))[List((A,Object), (ScalaObject,ScalaObject))])) -invalidate C.scala because parents have changed [Changed(Class(B))[List((A,Object), (ScalaObject,ScalaObject))]] +Changes: Map(class A -> List(), class B -> List(Changed(Class(B))[List((A,Object))])) +invalidate C.scala because parents have changed [Changed(Class(B))[List((A,Object))]] invalidate B.scala because it references invalid (no longer inherited) definition [ParentChanged(Class(C))] compiling Set(B.scala, C.scala) B.scala:3: error: type mismatch; diff --git a/test/files/jvm/manifests.check b/test/files/jvm/manifests.check index 54f504b929..453db81a3b 100644 --- a/test/files/jvm/manifests.check +++ b/test/files/jvm/manifests.check @@ -29,7 +29,7 @@ x=Foo, m=Foo[scala.collection.immutable.List[Int]] x=Foo, m=Foo[Foo[Int]] x=Foo, m=Foo[scala.collection.immutable.List[Foo[Int]]] -x=Test1$$anon$1, m=Object with Bar[java.lang.String] +x=Test1$$anon$1, m=Object with Bar[java.lang.String] with Object ()=() true=true diff --git a/test/files/neg/anyval-children.check b/test/files/neg/anyval-children.check index cbb5a2b1d1..f1149a5ce3 100644 --- a/test/files/neg/anyval-children.check +++ b/test/files/neg/anyval-children.check @@ -1,6 +1,4 @@ -anyval-children.scala:7: error: illegal inheritance; superclass Bippy - is not a subclass of the superclass Object - of the mixin trait ScalaObject +anyval-children.scala:7: error: Bippy does not have a constructor class NotOkBippy1 extends Bippy // fail ^ anyval-children.scala:9: error: illegal inheritance; superclass Bippy @@ -8,9 +6,7 @@ anyval-children.scala:9: error: illegal inheritance; superclass Bippy of the mixin trait Immutable class NotOkBippy2 extends Bippy with Immutable //fail ^ -anyval-children.scala:9: error: illegal inheritance; superclass Bippy - is not a subclass of the superclass Object - of the mixin trait ScalaObject +anyval-children.scala:9: error: Bippy does not have a constructor class NotOkBippy2 extends Bippy with Immutable //fail ^ anyval-children.scala:11: error: illegal inheritance; superclass Bippy @@ -18,9 +14,7 @@ anyval-children.scala:11: error: illegal inheritance; superclass Bippy of the mixin trait Immutable @inline class NotOkBippy3 extends Bippy with Immutable //fail ^ -anyval-children.scala:11: error: illegal inheritance; superclass Bippy - is not a subclass of the superclass Object - of the mixin trait ScalaObject +anyval-children.scala:11: error: Bippy does not have a constructor @inline class NotOkBippy3 extends Bippy with Immutable //fail ^ 5 errors found diff --git a/test/files/neg/t0764.check b/test/files/neg/t0764.check index 0788db7f6e..e14c7705b8 100644 --- a/test/files/neg/t0764.check +++ b/test/files/neg/t0764.check @@ -1,5 +1,5 @@ t0764.scala:13: error: type mismatch; - found : Object with Node{type T = _1.type} where val _1: Node{type T = NextType} + found : Node{type T = _1.type} where val _1: Node{type T = NextType} required: Node{type T = Main.this.AType} new Main[AType]( (value: AType).prepend ) ^ diff --git a/test/files/neg/t2641.check b/test/files/neg/t2641.check index 9e2f02ac47..909f4f0cf3 100644 --- a/test/files/neg/t2641.check +++ b/test/files/neg/t2641.check @@ -9,11 +9,7 @@ t2641.scala:17: error: illegal inheritance; self-type ManagedSeq does not conform to scala.collection.TraversableView[A,ManagedSeqStrict[A]]'s selftype scala.collection.TraversableView[A,ManagedSeqStrict[A]] with TraversableView[A, ManagedSeqStrict[A]] ^ -t2641.scala:16: error: illegal inheritance; - self-type ManagedSeq does not conform to ScalaObject's selftype ScalaObject - extends ManagedSeqStrict[A] - ^ t2641.scala:27: error: value managedIterator is not a member of ManagedSeq override def managedIterator = self.managedIterator slice (from, until) ^ -5 errors found +four errors found diff --git a/test/files/neg/t3691.check b/test/files/neg/t3691.check index cd7b440dce..bdf6c268b2 100644 --- a/test/files/neg/t3691.check +++ b/test/files/neg/t3691.check @@ -1,10 +1,10 @@ t3691.scala:4: error: type mismatch; - found : Object with Test.A[String] + found : Test.A[String] required: AnyRef{type A[x]} val b = (new A[String]{}): { type A[x] } // not ok ^ t3691.scala:5: error: type mismatch; - found : Object with Test.A[String] + found : Test.A[String] required: AnyRef{type A} val c = (new A[String]{}): { type A } // not ok ^ diff --git a/test/files/neg/t464-neg.check b/test/files/neg/t464-neg.check index aea1987b2e..e822e7fb6b 100644 --- a/test/files/neg/t464-neg.check +++ b/test/files/neg/t464-neg.check @@ -1,7 +1,7 @@ t464-neg.scala:7: error: not found: value f1 f1() ^ -t464-neg.scala:8: error: method f1 in class A cannot be accessed in A with ScalaObject +t464-neg.scala:8: error: method f1 in class A cannot be accessed in A super.f1() ^ t464-neg.scala:9: error: value f2 is not a member of B @@ -10,7 +10,7 @@ t464-neg.scala:9: error: value f2 is not a member of B t464-neg.scala:10: error: method f3 in class A cannot be accessed in B f3() ^ -t464-neg.scala:11: error: method f3 in class A cannot be accessed in A with ScalaObject +t464-neg.scala:11: error: method f3 in class A cannot be accessed in A super.f3() ^ 5 errors found diff --git a/test/files/neg/t4877.check b/test/files/neg/t4877.check index 0f72300bb4..a4b1e6a50d 100644 --- a/test/files/neg/t4877.check +++ b/test/files/neg/t4877.check @@ -9,7 +9,7 @@ t4877.scala:6: error: type mismatch; def foo3: AnyRef { def bar(x: Int): Int } = new AnyRef { def bar(x: Int) = "abc" } ^ t4877.scala:7: error: type mismatch; - found : Object with C{def bar(x: Int): Int} + found : C{def bar(x: Int): Int} required: C{def bar(x: Int): Int; def quux(x: Int): Int} def foo4: C { def bar(x: Int): Int ; def quux(x: Int): Int } = new C { def bar(x: Int) = 5 } ^ diff --git a/test/files/neg/t5060.check b/test/files/neg/t5060.check index ab860c9d5b..e71f30ccdb 100644 --- a/test/files/neg/t5060.check +++ b/test/files/neg/t5060.check @@ -1,7 +1,7 @@ -t5060.scala:2: error: covariant type T occurs in contravariant position in type => Object with ScalaObject{def contains(x: T): Unit} of value foo0 +t5060.scala:2: error: covariant type T occurs in contravariant position in type => Object{def contains(x: T): Unit} of value foo0 val foo0 = { ^ -t5060.scala:6: error: covariant type T occurs in contravariant position in type => Object with ScalaObject{def contains(x: T): Unit} of method foo1 +t5060.scala:6: error: covariant type T occurs in contravariant position in type => Object{def contains(x: T): Unit} of method foo1 def foo1 = { ^ two errors found diff --git a/test/files/neg/t664.check b/test/files/neg/t664.check index 43a6bea074..cbdf53daea 100644 --- a/test/files/neg/t664.check +++ b/test/files/neg/t664.check @@ -1,7 +1,7 @@ -t664.scala:4: error: type Foo is not a member of test.Test with ScalaObject +t664.scala:4: error: type Foo is not a member of test.Test trait Foo extends super.Foo { ^ -t664.scala:5: error: type Bar is not a member of AnyRef with ScalaObject +t664.scala:5: error: type Bar is not a member of AnyRef trait Bar extends super.Bar; ^ two errors found diff --git a/test/files/neg/variances.check b/test/files/neg/variances.check index 4eaab56cef..dc72b05e1e 100644 --- a/test/files/neg/variances.check +++ b/test/files/neg/variances.check @@ -4,7 +4,7 @@ variances.scala:4: error: covariant type A occurs in contravariant position in t variances.scala:14: error: covariant type A occurs in contravariant position in type A of value a private[this] def setA(a : A) = this.a = a ^ -variances.scala:16: error: covariant type A occurs in invariant position in supertype test.C[A] with ScalaObject of object Baz +variances.scala:16: error: covariant type A occurs in invariant position in supertype test.C[A] of object Baz object Baz extends C[A] ^ variances.scala:63: error: covariant type A occurs in contravariant position in type => test.Covariant.T[A]{val m: A => A} of value x diff --git a/test/files/pos/t1050.scala b/test/files/pos/t1050.scala index e017e30713..d34b0cff16 100644 --- a/test/files/pos/t1050.scala +++ b/test/files/pos/t1050.scala @@ -1,7 +1,7 @@ package t1050 abstract class A { - type T <: scala.ScalaObject + type T <: scala.AnyRef class A { this: T => def b = 3 def c = b diff --git a/test/files/presentation/callcc-interpreter.check b/test/files/presentation/callcc-interpreter.check index 3385ef12b7..41b07b07dc 100644 --- a/test/files/presentation/callcc-interpreter.check +++ b/test/files/presentation/callcc-interpreter.check @@ -20,7 +20,7 @@ retrieved 64 members `method ->[B](y: B)(callccInterpreter.type, B)` `method ==(x$1: Any)Boolean` `method ==(x$1: AnyRef)Boolean` -`method add(a: callccInterpreter.Value, b: callccInterpreter.Value)callccInterpreter.M[_ >: callccInterpreter.Num with callccInterpreter.Wrong.type <: Product with Serializable with callccInterpreter.Value]` +`method add(a: callccInterpreter.Value, b: callccInterpreter.Value)callccInterpreter.M[_ >: callccInterpreter.Num with callccInterpreter.Wrong.type <: Serializable with Product with callccInterpreter.Value]` `method apply(a: callccInterpreter.Value, b: callccInterpreter.Value)callccInterpreter.M[callccInterpreter.Value]` `method asInstanceOf[T0]=> T0` `method callCC[A](h: (A => callccInterpreter.M[A]) => callccInterpreter.M[A])callccInterpreter.M[A]` @@ -87,7 +87,7 @@ def showM(m: callccInterpreter.M[callccInterpreter.Value]): String = m.in.apply( askType at CallccInterpreter.scala(50,30) ================================================================================ [response] askTypeAt at (50,30) -def add(a: callccInterpreter.Value, b: callccInterpreter.Value): callccInterpreter.M[_ >: callccInterpreter.Num with callccInterpreter.Wrong.type <: Product with Serializable with callccInterpreter.Value] = scala.this.Predef.Pair.apply[callccInterpreter.Value, callccInterpreter.Value](a, b) match { +def add(a: callccInterpreter.Value, b: callccInterpreter.Value): callccInterpreter.M[_ >: callccInterpreter.Num with callccInterpreter.Wrong.type <: Serializable with Product with callccInterpreter.Value] = scala.this.Predef.Pair.apply[callccInterpreter.Value, callccInterpreter.Value](a, b) match { case scala.this.Predef.Pair.unapply[callccInterpreter.Value, callccInterpreter.Value]() ((n: Int)callccInterpreter.Num((m @ _)), (n: Int)callccInterpreter.Num((n @ _))) => this.unitM[callccInterpreter.Num](callccInterpreter.this.Num.apply(m.+(n))) case _ => callccInterpreter.this.unitM[callccInterpreter.Wrong.type](callccInterpreter.this.Wrong) } diff --git a/test/files/run/primitive-sigs-2.check b/test/files/run/primitive-sigs-2.check index c69d1b54a6..0af1434cea 100644 --- a/test/files/run/primitive-sigs-2.check +++ b/test/files/run/primitive-sigs-2.check @@ -1,4 +1,4 @@ -T interface scala.ScalaObject +T class java.lang.Object List(A, char, class java.lang.Object) a public java.lang.Object Arr.arr4(java.lang.Object[],scala.reflect.Manifest) diff --git a/test/files/run/repl-parens.check b/test/files/run/repl-parens.check index 944846541f..69f0a9ce30 100644 --- a/test/files/run/repl-parens.check +++ b/test/files/run/repl-parens.check @@ -66,7 +66,7 @@ scala> 55 ; () => 5 res13: () => Int = scala> () => { class X ; new X } -res14: () => Object with ScalaObject = +res14: () => Object = scala> diff --git a/test/files/run/t4110.check b/test/files/run/t4110.check index 8b005989de..dea7e5957c 100644 --- a/test/files/run/t4110.check +++ b/test/files/run/t4110.check @@ -1,2 +1,2 @@ -Object with Test$A with Test$B -Object with Test$A with Test$B +Object with Test$A with Test$B with Object +Object with Test$A with Test$B with Object diff --git a/test/files/run/t4172.check b/test/files/run/t4172.check index 95e3eb950d..da467e27ea 100644 --- a/test/files/run/t4172.check +++ b/test/files/run/t4172.check @@ -4,7 +4,7 @@ Type :help for more information. scala> scala> val c = { class C { override def toString = "C" }; ((new C, new C { def f = 2 })) } -c: (C, C{def f: Int}) forSome { type C <: Object with ScalaObject } = (C,C) +c: (C, C{def f: Int}) forSome { type C <: Object } = (C,C) scala> diff --git a/test/files/run/t4891.check b/test/files/run/t4891.check index 072f8df8d4..79fd7f6fbb 100644 --- a/test/files/run/t4891.check +++ b/test/files/run/t4891.check @@ -5,4 +5,3 @@ test.generic.C1 test.generic.C2 (m) public void test.generic.C1.m1() null -interface scala.ScalaObject -- cgit v1.2.3 From 513710c5277ef8870ba3f5c7c9bebe40068ce4af Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 4 Feb 2012 13:16:15 -0800 Subject: Simple test manipulating Any-derived traits. --- test/files/pos/trait-parents.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/files/pos/trait-parents.scala (limited to 'test/files/pos') diff --git a/test/files/pos/trait-parents.scala b/test/files/pos/trait-parents.scala new file mode 100644 index 0000000000..f6a2688751 --- /dev/null +++ b/test/files/pos/trait-parents.scala @@ -0,0 +1,16 @@ +trait Bip extends Any +trait Foo extends Any +trait Bar extends AnyRef +trait Quux + +object Test { + def f(x: Bip) = 1 + def g1(x: Foo with Bip) = f(x) + + def main(args: Array[String]): Unit = { + f(new Bip with Foo { }) + f(new Foo with Bip { }) + g1(new Bip with Foo { }) + g1(new Foo with Bip { }) + } +} -- cgit v1.2.3 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 +++++- test/files/neg/anyval-anyref-parent.check | 15 ++- test/files/neg/anyval-anyref-parent.scala | 7 +- test/files/neg/t3222.check | 6 +- test/files/pos/anyval-children.scala | 1 - test/files/run/Meter.scala | 21 +++- test/files/run/programmatic-main.check | 27 ++--- 14 files changed, 311 insertions(+), 164 deletions(-) delete mode 100644 test/files/pos/anyval-children.scala (limited to 'test/files/pos') 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 } diff --git a/test/files/neg/anyval-anyref-parent.check b/test/files/neg/anyval-anyref-parent.check index be895867ff..e1903f5fcc 100644 --- a/test/files/neg/anyval-anyref-parent.check +++ b/test/files/neg/anyval-anyref-parent.check @@ -1,14 +1,23 @@ +anyval-anyref-parent.scala:2: error: Only classes (not traits) are allowed to extend AnyVal +trait Foo2 extends AnyVal // fail + ^ anyval-anyref-parent.scala:5: error: Any does not have a constructor class Bar1 extends Any // fail ^ -anyval-anyref-parent.scala:9: error: illegal inheritance; superclass Any +anyval-anyref-parent.scala:6: error: Value class needs to have exactly one public val parameter +class Bar2(x: Int) extends AnyVal // fail + ^ +anyval-anyref-parent.scala:10: error: illegal inheritance; superclass Any is not a subclass of the superclass Object of the mixin trait Immutable trait Foo4 extends Any with Immutable // fail ^ -anyval-anyref-parent.scala:10: error: illegal inheritance; superclass AnyVal +anyval-anyref-parent.scala:11: error: illegal inheritance; superclass AnyVal is not a subclass of the superclass Object of the mixin trait Immutable trait Foo5 extends AnyVal with Immutable // fail ^ -three errors found +anyval-anyref-parent.scala:11: error: Only classes (not traits) are allowed to extend AnyVal +trait Foo5 extends AnyVal with Immutable // fail + ^ +6 errors found diff --git a/test/files/neg/anyval-anyref-parent.scala b/test/files/neg/anyval-anyref-parent.scala index 08568487a9..f927992e59 100644 --- a/test/files/neg/anyval-anyref-parent.scala +++ b/test/files/neg/anyval-anyref-parent.scala @@ -1,10 +1,11 @@ trait Foo1 extends Any -trait Foo2 extends AnyVal +trait Foo2 extends AnyVal // fail trait Foo3 extends AnyRef class Bar1 extends Any // fail -@inline class Bar2 extends AnyVal -class Bar3 extends AnyRef +class Bar2(x: Int) extends AnyVal // fail +class Bar3(val x: Int) extends AnyVal // fail +class Bar4 extends AnyRef trait Foo4 extends Any with Immutable // fail trait Foo5 extends AnyVal with Immutable // fail diff --git a/test/files/neg/t3222.check b/test/files/neg/t3222.check index b1e1e50448..e724024f45 100644 --- a/test/files/neg/t3222.check +++ b/test/files/neg/t3222.check @@ -1,7 +1,7 @@ -t3222.scala:1: error: not found: type B -@throws(classOf[B]) - ^ t3222.scala:4: error: not found: type D def foo(@throws(classOf[D]) x: Int) {} ^ +t3222.scala:1: error: not found: type B +@throws(classOf[B]) + ^ two errors found diff --git a/test/files/pos/anyval-children.scala b/test/files/pos/anyval-children.scala deleted file mode 100644 index 4ef10a094f..0000000000 --- a/test/files/pos/anyval-children.scala +++ /dev/null @@ -1 +0,0 @@ -@inline class Bippy extends AnyVal \ No newline at end of file diff --git a/test/files/run/Meter.scala b/test/files/run/Meter.scala index 0c30ddd41e..5700025880 100644 --- a/test/files/run/Meter.scala +++ b/test/files/run/Meter.scala @@ -10,10 +10,29 @@ trait Printable extends Any { def print: Unit = Console.print(this) } object Test extends App { + { + val x: Meter = new Meter(1) + val a: Object = x.asInstanceOf[Object] + val y: Meter = a.asInstanceOf[Meter] + + val u: Double = 1 + val b: Object = u.asInstanceOf[Object] + val v: Double = b.asInstanceOf[Double] + } + val x = new Meter(1) + val y = x println((x + x) / x) println((x + x) / 0.5) println((x < x).toString) - + + println("x.hashCode: "+x.hashCode) + println("x == 1: "+(x == 1)) + println("x == y: "+(x == y)) + assert(x.hashCode == (1.0).hashCode) + + val a: Any = x + val b: Any = y + println("a == b: "+(a == b)) } diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check index b5a54f5ea7..9ddd4a6e14 100644 --- a/test/files/run/programmatic-main.check +++ b/test/files/run/programmatic-main.check @@ -13,17 +13,18 @@ specialize 11 @specialized-driven class and method specialization explicitouter 12 this refs to outer pointers, translate patterns erasure 13 erase types, add interfaces for traits - lazyvals 14 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 15 move nested functions to top level - constructors 16 move field definitions into constructors - flatten 17 eliminate inner classes - mixin 18 mixin composition - cleanup 19 platform-specific cleanups, generate reflective calls - icode 20 generate portable intermediate code - inliner 21 optimization: do inlining -inlineExceptionHandlers 22 optimization: inline exception handlers - closelim 23 optimization: eliminate uncalled closures - dce 24 optimization: eliminate dead code - jvm 25 generate JVM bytecode - terminal 26 The last phase in the compiler chain + posterasure 14 clean up erased inline classes + lazyvals 15 allocate bitmaps, translate lazy vals into lazified defs + lambdalift 16 move nested functions to top level + constructors 17 move field definitions into constructors + flatten 18 eliminate inner classes + mixin 19 mixin composition + cleanup 20 platform-specific cleanups, generate reflective calls + icode 21 generate portable intermediate code + inliner 22 optimization: do inlining +inlineExceptionHandlers 23 optimization: inline exception handlers + closelim 24 optimization: eliminate uncalled closures + dce 25 optimization: eliminate dead code + jvm 26 generate JVM bytecode + terminal 27 The last phase in the compiler chain -- cgit v1.2.3 From 54e284d669418ebc6445bd0ec66804b9067f6dd3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 7 Mar 2012 14:13:03 +0100 Subject: Allows case classes as value classes --- .../tools/nsc/typechecker/SyntheticMethods.scala | 26 +++--- src/library/scala/Serializable.scala | 2 +- test/files/pos/t715/meredith_1.scala | 2 +- test/files/run/MeterCaseClass.check | 21 +++++ test/files/run/MeterCaseClass.scala | 99 ++++++++++++++++++++++ 5 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 test/files/run/MeterCaseClass.check create mode 100644 test/files/run/MeterCaseClass.scala (limited to 'test/files/pos') diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 9bee731e1e..1c78426c20 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -217,7 +217,7 @@ trait SyntheticMethods extends ast.TreeDSL { List( Product_productPrefix -> (() => constantNullary(nme.productPrefix, clazz.name.decode)), Product_productArity -> (() => constantNullary(nme.productArity, arity)), - Product_productElement -> (() => perElementMethod(nme.productElement, accessorLub)(Ident)), + Product_productElement -> (() => perElementMethod(nme.productElement, accessorLub)(Select(This(clazz), _))), Product_iterator -> (() => productIteratorMethod), Product_canEqual -> (() => canEqualMethod) // This is disabled pending a reimplementation which doesn't add any @@ -226,12 +226,21 @@ trait SyntheticMethods extends ast.TreeDSL { ) } + def valueClassMethods = List( + Any_hashCode -> (() => hashCodeDerivedValueClassMethod), + Any_equals -> (() => equalsDerivedValueClassMethod) + ) + def caseClassMethods = productMethods ++ productNMethods ++ Seq( Object_hashCode -> (() => forwardToRuntime(Object_hashCode)), Object_toString -> (() => forwardToRuntime(Object_toString)), Object_equals -> (() => equalsCaseClassMethod) ) + def valueCaseClassMethods = productMethods ++ productNMethods ++ valueClassMethods ++ Seq( + Any_toString -> (() => forwardToRuntime(Object_toString)) + ) + def caseObjectMethods = productMethods ++ Seq( Object_hashCode -> (() => constantMethod(nme.hashCode_, clazz.name.decode.hashCode)), Object_toString -> (() => constantMethod(nme.toString_, clazz.name.decode)) @@ -239,11 +248,6 @@ trait SyntheticMethods extends ast.TreeDSL { // Object_equals -> (() => createMethod(Object_equals)(m => This(clazz) ANY_EQ Ident(m.firstParam))) ) - def inlineClassMethods = List( - Any_hashCode -> (() => hashCodeDerivedValueClassMethod), - Any_equals -> (() => equalsDerivedValueClassMethod) - ) - /** 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 @@ -258,10 +262,12 @@ trait SyntheticMethods extends ast.TreeDSL { def synthesize(): List[Tree] = { val methods = ( - if (clazz.isDerivedValueClass) inlineClassMethods - else if (!clazz.isCase) Nil - else if (clazz.isModuleClass) caseObjectMethods - else caseClassMethods + if (clazz.isCase) + if (clazz.isDerivedValueClass) valueCaseClassMethods + else if (clazz.isModuleClass) caseObjectMethods + else caseClassMethods + else if (clazz.isDerivedValueClass) valueClassMethods + else Nil ) def impls = for ((m, impl) <- methods ; if !hasOverridingImplementation(m)) yield impl() diff --git a/src/library/scala/Serializable.scala b/src/library/scala/Serializable.scala index 9be258bb83..9b9456e0a0 100644 --- a/src/library/scala/Serializable.scala +++ b/src/library/scala/Serializable.scala @@ -11,4 +11,4 @@ package scala /** * Classes extending this trait are serializable across platforms (Java, .NET). */ -trait Serializable extends java.io.Serializable +trait Serializable extends Any with java.io.Serializable diff --git a/test/files/pos/t715/meredith_1.scala b/test/files/pos/t715/meredith_1.scala index 3ed2e57d7a..8261b9881a 100644 --- a/test/files/pos/t715/meredith_1.scala +++ b/test/files/pos/t715/meredith_1.scala @@ -3,7 +3,7 @@ package com.sap.dspace.model.othello; import scala.xml._ trait XMLRenderer { - type T <: {def getClass() : java.lang.Class[_]} + type T <: Any {def getClass() : java.lang.Class[_]} val valueTypes = List( classOf[java.lang.Boolean], diff --git a/test/files/run/MeterCaseClass.check b/test/files/run/MeterCaseClass.check new file mode 100644 index 0000000000..08370d2097 --- /dev/null +++ b/test/files/run/MeterCaseClass.check @@ -0,0 +1,21 @@ +2.0 +Meter(4.0) +false +x.isInstanceOf[Meter]: true +x.hashCode: 1072693248 +x == 1: false +x == y: true +a == b: true +testing native arrays +Array(Meter(1.0), Meter(2.0)) +Meter(1.0) +>>>Meter(1.0)<<< Meter(1.0) +>>>Meter(2.0)<<< Meter(2.0) +testing wrapped arrays +FlatArray(Meter(1.0), Meter(2.0)) +Meter(1.0) +>>>Meter(1.0)<<< Meter(1.0) +>>>Meter(2.0)<<< Meter(2.0) +FlatArray(Meter(2.0), Meter(3.0)) +ArrayBuffer(1.0, 2.0) +FlatArray(0.3048ft, 0.6096ft) diff --git a/test/files/run/MeterCaseClass.scala b/test/files/run/MeterCaseClass.scala new file mode 100644 index 0000000000..4f082b5252 --- /dev/null +++ b/test/files/run/MeterCaseClass.scala @@ -0,0 +1,99 @@ +package a { + case class Meter(underlying: Double) extends AnyVal with _root_.b.Printable { + def + (other: Meter): Meter = + new Meter(this.underlying + other.underlying) + def / (other: Meter): Double = this.underlying / other.underlying + def / (factor: Double): Meter = new Meter(this.underlying / factor) + def < (other: Meter): Boolean = this.underlying < other.underlying + def toFoot: Foot = new Foot(this.underlying * 0.3048) + override def print = { Console.print(">>>"); super.print; proprint } + } + + object Meter extends (Double => Meter) { + + implicit val boxings = new BoxingConversions[Meter, Double] { + def box(x: Double) = new Meter(x) + def unbox(m: Meter) = m.underlying + } + } + + class Foot(val unbox: Double) extends AnyVal { + def + (other: Foot): Foot = + new Foot(this.unbox + other.unbox) + override def toString = unbox.toString+"ft" + } + object Foot { + implicit val boxings = new BoxingConversions[Foot, Double] { + def box(x: Double) = new Foot(x) + def unbox(m: Foot) = m.unbox + } + } + +} +package b { + trait Printable extends Any { + def print: Unit = Console.print(this) + protected def proprint = Console.print("<<<") + } +} +import a._ +import _root_.b._ +object Test extends App { + + { + val x: Meter = new Meter(1) + val a: Object = x.asInstanceOf[Object] + val y: Meter = a.asInstanceOf[Meter] + + val u: Double = 1 + val b: Object = u.asInstanceOf[Object] + val v: Double = b.asInstanceOf[Double] + } + + val x = new Meter(1) + val y = x + println((x + x) / x) + println((x + x) / 0.5) + println((x < x).toString) + println("x.isInstanceOf[Meter]: "+x.isInstanceOf[Meter]) + + + println("x.hashCode: "+x.hashCode) + println("x == 1: "+(x == 1)) + println("x == y: "+(x == y)) + assert(x.hashCode == (1.0).hashCode) + + val a: Any = x + val b: Any = y + println("a == b: "+(a == b)) + + { println("testing native arrays") + val arr = Array(x, y + x) + println(arr.deep) + def foo[T <: Printable](x: Array[T]) { + for (i <- 0 until x.length) { x(i).print; println(" "+x(i)) } + } + val m = arr(0) + println(m) + foo(arr) + } + + { println("testing wrapped arrays") + import collection.mutable.FlatArray + val arr = FlatArray(x, y + x) + println(arr) + def foo(x: FlatArray[Meter]) { + for (i <- 0 until x.length) { x(i).print; println(" "+x(i)) } + } + val m = arr(0) + println(m) + foo(arr) + val ys: Seq[Meter] = arr map (_ + new Meter(1)) + println(ys) + val zs = arr map (_ / Meter(1)) + println(zs) + val fs = arr map (_.toFoot) + println(fs) + } + +} -- cgit v1.2.3 From acb2c851720141982514b11b4b34ba68dc868bf2 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 15 Mar 2012 09:28:07 -0700 Subject: New option -Ypos-debug, and fixed range position breakage. (Looks like there is more range position breakage yet, but this gets the outermost layer.) Channeling my struggles into a slightly easier future. % scalac -Ypos-debug -d /tmp ./src/library/scala/Predef.scala ./src/library/scala/Predef.scala:222: warning: Positioned tree has unpositioned child in phase extmethods def x = __resultOfEnsuring ^ parent: #7109 line 222 Select // (value __resultOfEnsuring in class Ensuring) child: #7108 Ident // (value $this) ./src/library/scala/Predef.scala:258: warning: Positioned tree has unpositioned child in phase extmethods def x = __leftOfArrow ^ parent: #7280 line 258 Select // (value __leftOfArrow in class ArrowAssoc) child: #7279 Ident // (value $this) two warnings found Or try this to really see some output: % scalac -Yrangepos -Ypos-debug --- src/compiler/scala/reflect/internal/Symbols.scala | 3 +- src/compiler/scala/reflect/internal/Trees.scala | 10 ++++-- src/compiler/scala/tools/nsc/ast/Trees.scala | 41 ++++++++++++++++++++++ .../scala/tools/nsc/ast/parser/Parsers.scala | 4 +-- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 2 +- .../tools/nsc/interactive/RangePositions.scala | 26 +++++++++----- .../scala/tools/nsc/settings/ScalaSettings.scala | 1 + .../scala/tools/nsc/transform/AddInterfaces.scala | 9 +++-- .../tools/nsc/transform/ExtensionMethods.scala | 26 ++++++-------- .../tools/nsc/typechecker/MethodSynthesis.scala | 3 ++ .../scala/tools/nsc/typechecker/Namers.scala | 5 +-- .../scala/tools/nsc/typechecker/RefChecks.scala | 9 ++--- .../tools/nsc/typechecker/SyntheticMethods.scala | 26 ++++++-------- .../scala/tools/nsc/typechecker/Typers.scala | 26 ++++++++------ src/library/scala/reflect/api/Trees.scala | 18 +++++++--- test/files/pos/anyval-rangepos.flags | 1 + test/files/pos/anyval-rangepos.scala | 1 + 17 files changed, 136 insertions(+), 75 deletions(-) create mode 100644 test/files/pos/anyval-rangepos.flags create mode 100644 test/files/pos/anyval-rangepos.scala (limited to 'test/files/pos') diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index febc2ef330..9678d2b8cd 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -500,8 +500,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isClassLocalToConstructor = isClass && hasFlag(INCONSTRUCTOR) final def isDerivedValueClass = - isClass && info.parents.headOption.getOrElse(AnyClass.tpe).typeSymbol == AnyValClass && - !isPrimitiveValueClass + isClass && info.firstParent.typeSymbol == AnyValClass && !isPrimitiveValueClass final def isMethodWithExtension = isMethod && owner.isDerivedValueClass && !isParamAccessor && !isConstructor && !hasFlag(SUPERACCESSOR) diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index 77ea7392a8..9b1712b790 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -283,10 +283,16 @@ trait Trees extends api.Trees { self: SymbolTable => } } - private object posAssigner extends Traverser { + trait PosAssigner extends Traverser { + var pos: Position + } + protected[this] lazy val posAssigner: PosAssigner = new DefaultPosAssigner + + protected class DefaultPosAssigner extends PosAssigner { var pos: Position = _ override def traverse(t: Tree) { - if (t != EmptyTree && t.pos == NoPosition) { + if (t eq EmptyTree) () + else if (t.pos == NoPosition) { t.setPos(pos) super.traverse(t) // TODO: bug? shouldn't the traverse be outside of the if? // @PP: it's pruning whenever it encounters a node with a diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index a1d3846557..43c231cf2d 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -16,6 +16,47 @@ import scala.reflect.internal.Flags.TRAIT trait Trees extends reflect.internal.Trees { self: Global => + def treeLine(t: Tree): String = + if (t.pos.isDefined && t.pos.isRange) t.pos.lineContent.drop(t.pos.column - 1).take(t.pos.end - t.pos.start + 1) + else t.summaryString + + def treeStatus(t: Tree, enclosingTree: Tree = null) = { + val parent = if (enclosingTree eq null) " " else " P#%5s".format(enclosingTree.id) + + "[L%4s%8s] #%-6s %-15s %-10s // %s".format(t.pos.safeLine, parent, t.id, t.pos.show, t.shortClass, treeLine(t)) + } + def treeSymStatus(t: Tree) = { + val line = if (t.pos.isDefined) "line %-4s".format(t.pos.safeLine) else " " + "#%-5s %s %-10s // %s".format(t.id, line, t.shortClass, + if (t.symbol ne NoSymbol) "(" + t.symbol.fullLocationString + ")" + else treeLine(t) + ) + } + + class ValidatingPosAssigner extends PosAssigner { + var pos: Position = _ + override def traverse(t: Tree) { + if (t eq EmptyTree) () + else if (t.pos == NoPosition) super.traverse(t setPos pos) + else if (globalPhase.id <= currentRun.picklerPhase.id) { + // When we prune due to encountering a position, traverse the + // pruned children so we can warn about those lacking positions. + t.children foreach { c => + if ((c eq EmptyTree) || (c eq emptyValDef)) () + else if (c.pos == NoPosition) { + reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase) + inform("parent: " + treeSymStatus(t)) + inform(" child: " + treeSymStatus(c) + "\n") + } + } + } + } + } + + override protected[this] lazy val posAssigner: PosAssigner = + if (settings.Yrangepos.value && settings.debug.value || settings.Yposdebug.value) new ValidatingPosAssigner + else new DefaultPosAssigner + // --- additional cases -------------------------------------------------------- /** Only used during parsing */ case class Parens(args: List[Tree]) extends Tree diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index ab6125df61..ccebcfa54d 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -394,7 +394,7 @@ self => // object Main def moduleName = newTermName(ScriptRunner scriptMain settings) - def moduleBody = Template(List(scalaAnyRefConstr), emptyValDef, List(emptyInit, mainDef)) + def moduleBody = Template(List(atPos(o2p(in.offset))(scalaAnyRefConstr)), emptyValDef, List(emptyInit, mainDef)) def moduleDef = ModuleDef(NoMods, moduleName, moduleBody) // package { ... } @@ -2738,7 +2738,7 @@ self => def anyrefParents() = { val caseParents = if (mods.isCase) List(productConstr, serializableConstr) else Nil parents0 ::: caseParents match { - case Nil => List(scalaAnyRefConstr) + case Nil => List(atPos(o2p(in.offset))(scalaAnyRefConstr)) case ps => ps } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 0d2fbc5372..661f893c59 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -206,7 +206,7 @@ abstract class TreeBuilder { def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree], argss: List[List[Tree]], npos: Position, cpos: Position): Tree = if (parents.isEmpty) - makeNew(List(scalaAnyRefConstr), self, stats, argss, npos, cpos) + makeNew(List(atPos(npos union cpos)(scalaAnyRefConstr)), self, stats, argss, npos, cpos) else if (parents.tail.isEmpty && stats.isEmpty) atPos(npos union cpos) { New(parents.head, argss) } else { diff --git a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala index d1f738a435..d08a363a9d 100644 --- a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala +++ b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala @@ -168,7 +168,7 @@ self: scala.tools.nsc.Global => /** Position a tree. * This means: Set position of a node and position all its unpositioned children. */ - override def atPos[T <: Tree](pos: Position)(tree: T): T = + override def atPos[T <: Tree](pos: Position)(tree: T): T = { if (pos.isOpaqueRange) { if (!tree.isEmpty && tree.pos == NoPosition) { tree.setPos(pos) @@ -182,6 +182,7 @@ self: scala.tools.nsc.Global => } else { super.atPos(pos)(tree) } + } // ---------------- Validating positions ---------------------------------- @@ -190,26 +191,33 @@ self: scala.tools.nsc.Global => val source = if (tree.pos.isDefined) tree.pos.source else "" inform("== "+prefix+" tree ["+tree.id+"] of type "+tree.productPrefix+" at "+tree.pos.show+source) inform("") - inform(tree.toString) + inform(treeStatus(tree)) inform("") } def positionError(msg: String)(body : => Unit) { - inform("======= Bad positions: "+msg) - inform("") + inform("======= Position error\n" + msg) body - inform("=== While validating") - inform("") - inform(tree.toString) - inform("") + inform("\nWhile validating #" + tree.id) + inform(treeStatus(tree)) + inform("\nChildren:") + tree.children map (t => " " + treeStatus(t, tree)) foreach inform inform("=======") throw new ValidateException(msg) } def validate(tree: Tree, encltree: Tree): Unit = { + if (!tree.isEmpty) { + if (settings.Yposdebug.value && (settings.verbose.value || settings.Yrangepos.value)) + println("[%10s] %s".format("validate", treeStatus(tree, encltree))) + if (!tree.pos.isDefined) - positionError("Unpositioned tree ["+tree.id+"]") { reportTree("Unpositioned", tree) } + positionError("Unpositioned tree #"+tree.id) { + inform("%15s %s".format("unpositioned", treeStatus(tree, encltree))) + inform("%15s %s".format("enclosing", treeStatus(encltree))) + encltree.children foreach (t => inform("%15s %s".format("sibling", treeStatus(t, encltree)))) + } if (tree.pos.isRange) { if (!encltree.pos.isRange) positionError("Synthetic tree ["+encltree.id+"] contains nonsynthetic tree ["+tree.id+"]") { diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index e949cb3eb2..5f3c7ec32c 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -162,6 +162,7 @@ trait ScalaSettings extends AbsScalaSettings val Ybuildmanagerdebug = BooleanSetting ("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.") val Ytyperdebug = BooleanSetting ("-Ytyper-debug", "Trace all type assignments.") + val Yposdebug = BooleanSetting ("-Ypos-debug", "Trace position validation.") val Yinferdebug = BooleanSetting ("-Yinfer-debug", "Trace type inference and implicit search.") val Ypmatdebug = BooleanSetting ("-Ypmat-debug", "Trace all pattern matcher activity.") val Yreifycopypaste = diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 555d0700ae..39e2cbe694 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -264,11 +264,10 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => else DefDef(clazz.primaryConstructor, Block(List(), Literal(Constant()))) :: stats private def implTemplate(clazz: Symbol, templ: Template): Template = atPos(templ.pos) { - val templ1 = atPos(templ.pos) { - Template(templ.parents, emptyValDef, - addMixinConstructorDef(clazz, templ.body map implMemberDef)) - .setSymbol(clazz.newLocalDummy(templ.pos)) - } + val templ1 = ( + Template(templ.parents, emptyValDef, addMixinConstructorDef(clazz, templ.body map implMemberDef)) + setSymbol clazz.newLocalDummy(templ.pos) + ) templ1.changeOwner(templ.symbol.owner -> clazz, templ.symbol -> templ1.symbol) templ1 } diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index e6ad7cb922..4c3972519a 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -105,7 +105,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { } else if (currentOwner.isStaticOwner) { super.transform(tree) } else tree - case DefDef(mods, name, tparams, vparamss, tpt, rhs) if tree.symbol.isMethodWithExtension => + case DefDef(_, _, tparams, vparamss, _, rhs) if tree.symbol.isMethodWithExtension => val companion = currentOwner.companionModule val origMeth = tree.symbol val extensionName = extensionNames(origMeth).head @@ -132,15 +132,13 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { gen.mkTypeApply(gen.mkAttributedRef(companion), extensionMeth, origTpeParams map (_.tpe)), List(This(currentOwner))) val extensionCall = atOwner(origMeth) { - localTyper.typed { - atPos(rhs.pos) { - (extensionCallPrefix /: vparamss) { - case (fn, params) => Apply(fn, params map (param => Ident(param.symbol))) - } + localTyper.typedPos(rhs.pos) { + (extensionCallPrefix /: vparamss) { + case (fn, params) => Apply(fn, params map (param => Ident(param.symbol))) } } } - treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, extensionCall) + deriveDefDef(tree)(_ => extensionCall) case _ => super.transform(tree) } @@ -148,14 +146,12 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = super.transformStats(stats, exprOwner) map { - case stat @ ModuleDef(mods, name, tmpl @ Template(parents, self, body)) => - extensionDefs.remove(stat.symbol) match { - case Some(buf) => - val extensionDefs = buf.toList map { mdef => atOwner(stat.symbol) { localTyper.typed(mdef) } } - treeCopy.ModuleDef(stat, mods, name, treeCopy.Template(tmpl, parents, self, body ++ extensionDefs)) - case None => - stat - } + case md @ ModuleDef(_, _, _) if extensionDefs contains md.symbol => + val defns = extensionDefs(md.symbol).toList map (member => + atOwner(md.symbol)(localTyper.typedPos(md.pos.focus)(member)) + ) + extensionDefs -= md.symbol + deriveModuleDef(md)(tmpl => deriveTemplate(tmpl)(_ ++ defns)) case stat => stat } diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 088a56cd7b..f32ad9293c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -69,6 +69,9 @@ trait MethodSynthesis { import synthesisUtil._ class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { + def mkThis = This(clazz) setPos clazz.pos.focus + def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)(Select(mkThis, sym)) + private def isOverride(name: TermName) = clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 955d51bf8d..c5fb13a5a9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -456,7 +456,7 @@ trait Namers extends MethodSynthesis { // The object Foo is still in scope, but because it is not compiled in current run // it should be ditched and a new one created. if (m != NoSymbol && currentRun.compiles(m)) m - else enterSyntheticSym(creator(cdef)) + else enterSyntheticSym(atPos(cdef.pos.focus)(creator(cdef))) } private def checkSelectors(tree: Import): Unit = { @@ -1270,11 +1270,12 @@ trait Namers extends MethodSynthesis { if (sym.isModule) annotate(sym.moduleClass) def getSig = tree match { - case cdef @ ClassDef(_, _, tparams, impl) => + case cdef @ ClassDef(_, name, tparams, impl) => val clazz = tree.symbol val result = createNamer(tree).classSig(tparams, impl) clazz setInfo result if (clazz.isDerivedValueClass) { + log("Ensuring companion for derived value class " + name + " at " + cdef.pos.show) clazz setFlag FINAL enclosingNamerWithScope(clazz.owner.info.decls).ensureCompanionObject(cdef) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 1e17cb2e3f..ec42d251ff 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1523,16 +1523,11 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case _ => () } - // verify classes extending AnyVal meet the requirements - // (whatever those are to be, but at least: @inline annotation) + // Verify classes extending AnyVal meet the requirements private def checkAnyValSubclass(clazz: Symbol) = { - if ((clazz isSubClass AnyValClass) && (clazz ne AnyValClass) && !isPrimitiveValueClass(clazz)) { + if ((clazz isSubClass AnyValClass) && !isPrimitiveValueClass(clazz)) { if (clazz.isTrait) unit.error(clazz.pos, "Only classes (not traits) are allowed to extend AnyVal") - /* [Martin] That one is already taken care of by Typers - if (clazz.tpe <:< AnyRefClass.tpe) - unit.error(clazz.pos, "Classes which extend AnyVal may not have an ancestor which inherits AnyRef") - */ } } diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index f9d41bcc5e..2f4eff30d2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -51,11 +51,9 @@ trait SyntheticMethods extends ast.TreeDSL { 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 - NULL - } - treeCopy.Template(templ, templ.parents, templ.self, templ.body :+ getClassMethod) + // XXX dummy implementation for now + val getClassMethod = createMethod(nme.getClass_, getClassReturnType(clazz.tpe))(_ => NULL) + deriveTemplate(templ)(_ :+ getClassMethod) } else templ } @@ -89,7 +87,7 @@ trait SyntheticMethods extends ast.TreeDSL { ) def forwardToRuntime(method: Symbol): Tree = - forwardMethod(method, getMember(ScalaRunTimeModule, method.name prepend "_"))(This(clazz) :: _) + forwardMethod(method, getMember(ScalaRunTimeModule, method.name prepend "_"))(mkThis :: _) // Any member, including private def hasConcreteImpl(name: Name) = @@ -109,7 +107,7 @@ trait SyntheticMethods extends ast.TreeDSL { } def productIteratorMethod = { createMethod(nme.productIterator, iteratorOfType(accessorLub))(_ => - gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(accessorLub), List(This(clazz))) + gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(accessorLub), List(mkThis)) ) } def projectionMethod(accessor: Symbol, num: Int) = { @@ -157,8 +155,8 @@ trait SyntheticMethods extends ast.TreeDSL { 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 pairwise = accessors map (acc => fn(Select(mkThis, acc), acc.tpe member nme.EQ, Select(Ident(otherSym), acc))) + val canEq = gen.mkMethodCall(otherSym, nme.canEqual_, Nil, List(mkThis)) val tests = if (clazz.isDerivedValueClass || clazz.isFinal && syntheticCanEqual) pairwise else pairwise :+ canEq thatTest(eqmeth) AND Block( @@ -181,9 +179,9 @@ trait SyntheticMethods extends ast.TreeDSL { def equalsCaseClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => if (accessors.isEmpty) if (clazz.isFinal) thatTest(m) - else thatTest(m) AND ((thatCast(m) DOT nme.canEqual_)(This(clazz))) + else thatTest(m) AND ((thatCast(m) DOT nme.canEqual_)(mkThis)) else - (This(clazz) ANY_EQ Ident(m.firstParam)) OR equalsCore(m, accessors) + (mkThis ANY_EQ Ident(m.firstParam)) OR equalsCore(m, accessors) } /** The equality method for value classes @@ -200,9 +198,7 @@ trait SyntheticMethods extends ast.TreeDSL { * def hashCode(): Int = this.underlying.hashCode */ def hashCodeDerivedValueClassMethod: Tree = createMethod(nme.hashCode_, Nil, IntClass.tpe) { m => - Select( - Select(This(clazz), clazz.firstParamAccessor), - nme.hashCode_) + Select(mkThisSelect(clazz.firstParamAccessor), nme.hashCode_) } /** The _1, _2, etc. methods to implement ProductN. @@ -217,7 +213,7 @@ trait SyntheticMethods extends ast.TreeDSL { List( Product_productPrefix -> (() => constantNullary(nme.productPrefix, clazz.name.decode)), Product_productArity -> (() => constantNullary(nme.productArity, arity)), - Product_productElement -> (() => perElementMethod(nme.productElement, accessorLub)(Select(This(clazz), _))), + Product_productElement -> (() => perElementMethod(nme.productElement, accessorLub)(mkThisSelect)), Product_iterator -> (() => productIteratorMethod), Product_canEqual -> (() => canEqualMethod) // This is disabled pending a reimplementation which doesn't add any diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9a2ef88821..973e26af41 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1295,7 +1295,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def parentTypes(templ: Template): List[Tree] = - if (templ.parents.isEmpty) List(TypeTree(AnyRefClass.tpe)) + if (templ.parents.isEmpty) List(atPos(templ.pos.focus)(TypeTree(AnyRefClass.tpe))) else try { val clazz = context.owner // Normalize supertype and mixins so that supertype is always a class, not a trait. @@ -1723,7 +1723,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * @param rhs ... */ def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree) { - debuglog("computing param aliases for "+clazz+":"+clazz.primaryConstructor.tpe+":"+rhs)//debug + log("computing param aliases for "+clazz+":"+clazz.primaryConstructor.tpe+":"+rhs)//debug def decompose(call: Tree): (Tree, List[Tree]) = call match { case Apply(fn, args) => val (superConstr, args1) = decompose(fn) @@ -1902,11 +1902,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe) } - if (meth.isPrimaryConstructor && meth.isClassConstructor && !isPastTyper && !reporter.hasErrors && !meth.owner.isSubClass(AnyValClass)) { - // At this point in AnyVal there is no supercall, which will blow up - // in computeParamAliases; there's nothing to be computed for Anyval anyway. - computeParamAliases(meth.owner, vparamss1, rhs1) - } + if (meth.isPrimaryConstructor && meth.isClassConstructor && !isPastTyper && !reporter.hasErrors && !meth.owner.isSubClass(AnyValClass)) { + // At this point in AnyVal there is no supercall, which will blow up + // in computeParamAliases; there's nothing to be computed for Anyval anyway. + computeParamAliases(meth.owner, vparamss1, rhs1) + } + if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass) rhs1 = checkDead(rhs1) @@ -1925,6 +1926,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (meth.isStructuralRefinementMember) checkMethodStructuralCompatible(meth) + treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType } @@ -3264,6 +3266,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def typedAnnotated(ann: Tree, arg1: Tree): Tree = { + def mkTypeTree(tpe: Type) = TypeTree(tpe) setOriginal tree setPos tree.pos.focus /** mode for typing the annotation itself */ val annotMode = mode & ~TYPEmode | EXPRmode @@ -3309,19 +3312,20 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { arg1 // simply drop erroneous annotations else { ann.tpe = atype - TypeTree(atype) setOriginal tree + mkTypeTree(atype) } } else { // the annotation was typechecked before - TypeTree(ann.tpe) setOriginal tree + mkTypeTree(ann.tpe) } - } else { + } + else { if (ann.tpe == null) { val annotInfo = typedAnnotation(ann, annotMode) ann.tpe = arg1.tpe.withAnnotation(annotInfo) } val atype = ann.tpe - Typed(arg1, TypeTree(atype) setOriginal tree setPos tree.pos.focus) setPos tree.pos setType atype + Typed(arg1, mkTypeTree(atype)) setPos tree.pos setType atype } } diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index 181ce85dac..a355207ff0 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -309,7 +309,7 @@ trait Trees { self: Universe => * quite frequently called modules to reduce ambiguity. */ case class ModuleDef(mods: Modifiers, name: TermName, impl: Template) - extends ImplDef + extends ImplDef /** A common base class for ValDefs and DefDefs. */ @@ -319,8 +319,13 @@ trait Trees { self: Universe => def rhs: Tree } - /** A value definition (this includes vars as well, which differ from - * vals only in having the MUTABLE flag set in their Modifiers.) + /** Broadly speaking, a value definition. All these are encoded as ValDefs: + * + * - immutable values, e.g. "val x" + * - mutable values, e.g. "var x" - the MUTABLE flag set in mods + * - lazy values, e.g. "lazy val x" - the LAZY flag set in mods + * - method parameters, see vparamss in DefDef - the PARAM flag is set in mods + * - explicit self-types, e.g. class A { self: Bar => } - !!! not sure what is set. */ case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) extends ValOrDefDef @@ -390,7 +395,6 @@ trait Trees { self: Universe => // { // def bar // owner is local dummy // } - // System.err.println("TEMPLATE: " + parents) } /** Block of expressions (semicolon separated expressions) */ @@ -741,6 +745,12 @@ trait Trees { self: Universe => case t => sys.error("Not a ClassDef: " + t + "/" + t.getClass) } + def deriveModuleDef(mdef: Tree)(applyToImpl: Template => Template): ModuleDef = mdef match { + case ModuleDef(mods0, name0, impl0) => + treeCopy.ModuleDef(mdef, mods0, name0, applyToImpl(impl0)) + case t => + sys.error("Not a ModuleDef: " + t + "/" + t.getClass) + } def deriveCaseDef(cdef: Tree)(applyToBody: Tree => Tree): CaseDef = cdef match { case CaseDef(pat0, guard0, body0) => treeCopy.CaseDef(cdef, pat0, guard0, applyToBody(body0)) diff --git a/test/files/pos/anyval-rangepos.flags b/test/files/pos/anyval-rangepos.flags new file mode 100644 index 0000000000..fcf951d907 --- /dev/null +++ b/test/files/pos/anyval-rangepos.flags @@ -0,0 +1 @@ +-Yrangepos \ No newline at end of file diff --git a/test/files/pos/anyval-rangepos.scala b/test/files/pos/anyval-rangepos.scala new file mode 100644 index 0000000000..8d79793c0d --- /dev/null +++ b/test/files/pos/anyval-rangepos.scala @@ -0,0 +1 @@ +class Foo(val x: Double) extends AnyVal { } -- cgit v1.2.3 From f987afe55e6d4f71c7e9ad10d1ca9f6120dc1132 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 16 Mar 2012 00:46:21 -0700 Subject: More uniformity for the parser. Type application and operator notation could not formerly be mixed. Now they can, as the grammar has always suggested. --- .../scala/tools/nsc/ast/parser/Parsers.scala | 33 ++++++++++++++++------ .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 20 +++++++++---- test/files/pos/dotless-targs.scala | 12 ++++++++ 3 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 test/files/pos/dotless-targs.scala (limited to 'test/files/pos') diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index ccebcfa54d..cd64c49b47 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -126,7 +126,7 @@ self => val global: Global import global._ - case class OpInfo(operand: Tree, operator: Name, offset: Offset) + case class OpInfo(operand: Tree, operator: Name, targs: List[Tree], offset: Offset) class SourceFileParser(val source: SourceFile) extends Parser { @@ -789,7 +789,7 @@ self => val rPos = top.pos val end = if (rPos.isDefined) rPos.endOrPoint else opPos.endOrPoint top = atPos(start, opinfo.offset, end) { - makeBinop(isExpr, opinfo.operand, opinfo.operator, top, opPos) + makeBinop(isExpr, opinfo.operand, opinfo.operator, top, opPos, opinfo.targs) } } top @@ -1440,6 +1440,17 @@ self => } } + def advanceStack(base: List[OpInfo], top: Tree): Tree = { + val newTop = reduceStack(true, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)) + val op = in.name + val pos = in.offset + ident() + val targs = if (in.token == LBRACKET) exprTypeArgs() else Nil + opstack ::= OpInfo(newTop, op, targs, pos) + + newTop + } + /** {{{ * PostfixExpr ::= InfixExpr [Id [nl]] * InfixExpr ::= PrefixExpr @@ -1451,22 +1462,21 @@ self => var top = prefixExpr() while (isIdent) { - top = reduceStack(true, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)) - val op = in.name - opstack = OpInfo(top, op, in.offset) :: opstack - ident() + top = advanceStack(base, top) newLineOptWhenFollowing(isExprIntroToken) + if (isExprIntro) { val next = prefixExpr() if (next == EmptyTree) return reduceStack(true, base, top, 0, true) top = next - } else { + } + else { val topinfo = opstack.head opstack = opstack.tail val od = stripParens(reduceStack(true, base, topinfo.operand, 0, true)) return atPos(od.pos.startOrPoint, topinfo.offset) { - Select(od, topinfo.operator.encode) + applyTypeArgs(Select(od, topinfo.operator.encode), topinfo.targs) } } } @@ -1806,7 +1816,7 @@ self => top = reduceStack( false, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)) val op = in.name - opstack = OpInfo(top, op, in.offset) :: opstack + opstack = OpInfo(top, op, Nil, in.offset) :: opstack ident() top = simplePattern() } @@ -1896,6 +1906,11 @@ self => def exprTypeArgs() = outPattern.typeArgs() def exprSimpleType() = outPattern.simpleType() + def applyTypeArgs(sel: Tree, targs: List[Tree]): Tree = ( + if (targs.isEmpty) sel + else atPos(sel.pos)(TypeApply(sel, targs)) + ) + /** Default entry points into some pattern contexts. */ def pattern(): Tree = noSeq.pattern() def patterns(): List[Tree] = noSeq.patterns() diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 661f893c59..87251b4cc9 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -174,7 +174,14 @@ abstract class TreeBuilder { } /** Create tree representing (unencoded) binary operation expression or pattern. */ - def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = { + def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = { + require(isExpr || targs.isEmpty, ((left, op, targs, right))) + + def mkSel(t: Tree) = { + val sel = atPos(opPos union t.pos)(Select(stripParens(t), op.encode)) + if (targs.isEmpty) sel else atPos(left.pos)(TypeApply(sel, targs)) + } + def mkNamed(args: List[Tree]) = if (isExpr) args map { case a @ Assign(id @ Ident(name), rhs) => @@ -187,14 +194,17 @@ abstract class TreeBuilder { } if (isExpr) { if (treeInfo.isLeftAssoc(op)) { - Apply(atPos(opPos union left.pos) { Select(stripParens(left), op.encode) }, arguments) - } else { + Apply(mkSel(left), arguments) + } + else { val x = freshTermName() Block( List(ValDef(Modifiers(SYNTHETIC), x, TypeTree(), stripParens(left))), - Apply(atPos(opPos union right.pos) { Select(stripParens(right), op.encode) }, List(Ident(x)))) + Apply(mkSel(right), List(Ident(x))) + ) } - } else { + } + else { Apply(Ident(op.encode), stripParens(left) :: arguments) } } diff --git a/test/files/pos/dotless-targs.scala b/test/files/pos/dotless-targs.scala new file mode 100644 index 0000000000..8337352d18 --- /dev/null +++ b/test/files/pos/dotless-targs.scala @@ -0,0 +1,12 @@ +class A { + def fn1 = List apply 1 + def fn2 = List apply[Int] 2 + + def f1 = "f1" isInstanceOf[String] + + def g1 = "g1" toList + def g2 = "g2" toList 2 + def g3 = "g3" apply 3 + + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) +} -- cgit v1.2.3 From bc7bf663f2df564805fa5121de7b0006cf2149f2 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 16 Mar 2012 06:32:46 -0700 Subject: Finish fixing range positions. At least, I think so. --- src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala | 2 +- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 +- test/files/pos/anyval-rangepos.flags | 1 - test/files/pos/anyval-rangepos.scala | 1 - test/files/pos/rangepos.flags | 1 + test/files/pos/rangepos.scala | 5 +++++ 6 files changed, 8 insertions(+), 4 deletions(-) delete mode 100644 test/files/pos/anyval-rangepos.flags delete mode 100644 test/files/pos/anyval-rangepos.scala create mode 100644 test/files/pos/rangepos.flags create mode 100644 test/files/pos/rangepos.scala (limited to 'test/files/pos') diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 87251b4cc9..4b7c03b72a 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -216,7 +216,7 @@ abstract class TreeBuilder { def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree], argss: List[List[Tree]], npos: Position, cpos: Position): Tree = if (parents.isEmpty) - makeNew(List(atPos(npos union cpos)(scalaAnyRefConstr)), self, stats, argss, npos, cpos) + makeNew(List(scalaAnyRefConstr), self, stats, argss, npos, cpos) else if (parents.tail.isEmpty && stats.isEmpty) atPos(npos union cpos) { New(parents.head, argss) } else { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 973e26af41..c4880dd6d7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1295,7 +1295,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def parentTypes(templ: Template): List[Tree] = - if (templ.parents.isEmpty) List(atPos(templ.pos.focus)(TypeTree(AnyRefClass.tpe))) + if (templ.parents.isEmpty) List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) else try { val clazz = context.owner // Normalize supertype and mixins so that supertype is always a class, not a trait. diff --git a/test/files/pos/anyval-rangepos.flags b/test/files/pos/anyval-rangepos.flags deleted file mode 100644 index fcf951d907..0000000000 --- a/test/files/pos/anyval-rangepos.flags +++ /dev/null @@ -1 +0,0 @@ --Yrangepos \ No newline at end of file diff --git a/test/files/pos/anyval-rangepos.scala b/test/files/pos/anyval-rangepos.scala deleted file mode 100644 index 8d79793c0d..0000000000 --- a/test/files/pos/anyval-rangepos.scala +++ /dev/null @@ -1 +0,0 @@ -class Foo(val x: Double) extends AnyVal { } diff --git a/test/files/pos/rangepos.flags b/test/files/pos/rangepos.flags new file mode 100644 index 0000000000..fcf951d907 --- /dev/null +++ b/test/files/pos/rangepos.flags @@ -0,0 +1 @@ +-Yrangepos \ No newline at end of file diff --git a/test/files/pos/rangepos.scala b/test/files/pos/rangepos.scala new file mode 100644 index 0000000000..623b096acb --- /dev/null +++ b/test/files/pos/rangepos.scala @@ -0,0 +1,5 @@ +class Foo(val x: Double) extends AnyVal { } + +object Pretty { + def f(s1: String) = new { def bar = 5 } +} -- cgit v1.2.3 From 87992a842e8c7bd1ee0e5acadefaf3e84848e835 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 16 Mar 2012 10:00:17 -0700 Subject: Revert "More uniformity for the parser." This reverts commit f987afe55e6d4f71c7e9ad10d1ca9f6120dc1132. Looks like somebody misread the grammar. Look for it to return in one of paulp's exclusive branches for today's discriminating hacker. --- .../scala/tools/nsc/ast/parser/Parsers.scala | 33 ++++++---------------- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 20 ++++--------- test/files/pos/dotless-targs.scala | 12 -------- 3 files changed, 14 insertions(+), 51 deletions(-) delete mode 100644 test/files/pos/dotless-targs.scala (limited to 'test/files/pos') diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index cd64c49b47..ccebcfa54d 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -126,7 +126,7 @@ self => val global: Global import global._ - case class OpInfo(operand: Tree, operator: Name, targs: List[Tree], offset: Offset) + case class OpInfo(operand: Tree, operator: Name, offset: Offset) class SourceFileParser(val source: SourceFile) extends Parser { @@ -789,7 +789,7 @@ self => val rPos = top.pos val end = if (rPos.isDefined) rPos.endOrPoint else opPos.endOrPoint top = atPos(start, opinfo.offset, end) { - makeBinop(isExpr, opinfo.operand, opinfo.operator, top, opPos, opinfo.targs) + makeBinop(isExpr, opinfo.operand, opinfo.operator, top, opPos) } } top @@ -1440,17 +1440,6 @@ self => } } - def advanceStack(base: List[OpInfo], top: Tree): Tree = { - val newTop = reduceStack(true, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)) - val op = in.name - val pos = in.offset - ident() - val targs = if (in.token == LBRACKET) exprTypeArgs() else Nil - opstack ::= OpInfo(newTop, op, targs, pos) - - newTop - } - /** {{{ * PostfixExpr ::= InfixExpr [Id [nl]] * InfixExpr ::= PrefixExpr @@ -1462,21 +1451,22 @@ self => var top = prefixExpr() while (isIdent) { - top = advanceStack(base, top) + top = reduceStack(true, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)) + val op = in.name + opstack = OpInfo(top, op, in.offset) :: opstack + ident() newLineOptWhenFollowing(isExprIntroToken) - if (isExprIntro) { val next = prefixExpr() if (next == EmptyTree) return reduceStack(true, base, top, 0, true) top = next - } - else { + } else { val topinfo = opstack.head opstack = opstack.tail val od = stripParens(reduceStack(true, base, topinfo.operand, 0, true)) return atPos(od.pos.startOrPoint, topinfo.offset) { - applyTypeArgs(Select(od, topinfo.operator.encode), topinfo.targs) + Select(od, topinfo.operator.encode) } } } @@ -1816,7 +1806,7 @@ self => top = reduceStack( false, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)) val op = in.name - opstack = OpInfo(top, op, Nil, in.offset) :: opstack + opstack = OpInfo(top, op, in.offset) :: opstack ident() top = simplePattern() } @@ -1906,11 +1896,6 @@ self => def exprTypeArgs() = outPattern.typeArgs() def exprSimpleType() = outPattern.simpleType() - def applyTypeArgs(sel: Tree, targs: List[Tree]): Tree = ( - if (targs.isEmpty) sel - else atPos(sel.pos)(TypeApply(sel, targs)) - ) - /** Default entry points into some pattern contexts. */ def pattern(): Tree = noSeq.pattern() def patterns(): List[Tree] = noSeq.patterns() diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 4b7c03b72a..0d2fbc5372 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -174,14 +174,7 @@ abstract class TreeBuilder { } /** Create tree representing (unencoded) binary operation expression or pattern. */ - def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = { - require(isExpr || targs.isEmpty, ((left, op, targs, right))) - - def mkSel(t: Tree) = { - val sel = atPos(opPos union t.pos)(Select(stripParens(t), op.encode)) - if (targs.isEmpty) sel else atPos(left.pos)(TypeApply(sel, targs)) - } - + def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = { def mkNamed(args: List[Tree]) = if (isExpr) args map { case a @ Assign(id @ Ident(name), rhs) => @@ -194,17 +187,14 @@ abstract class TreeBuilder { } if (isExpr) { if (treeInfo.isLeftAssoc(op)) { - Apply(mkSel(left), arguments) - } - else { + Apply(atPos(opPos union left.pos) { Select(stripParens(left), op.encode) }, arguments) + } else { val x = freshTermName() Block( List(ValDef(Modifiers(SYNTHETIC), x, TypeTree(), stripParens(left))), - Apply(mkSel(right), List(Ident(x))) - ) + Apply(atPos(opPos union right.pos) { Select(stripParens(right), op.encode) }, List(Ident(x)))) } - } - else { + } else { Apply(Ident(op.encode), stripParens(left) :: arguments) } } diff --git a/test/files/pos/dotless-targs.scala b/test/files/pos/dotless-targs.scala deleted file mode 100644 index 8337352d18..0000000000 --- a/test/files/pos/dotless-targs.scala +++ /dev/null @@ -1,12 +0,0 @@ -class A { - def fn1 = List apply 1 - def fn2 = List apply[Int] 2 - - def f1 = "f1" isInstanceOf[String] - - def g1 = "g1" toList - def g2 = "g2" toList 2 - def g3 = "g3" apply 3 - - def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) -} -- cgit v1.2.3 From b27a31b87d5ae42a51ee026d3d4fa181249ec669 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 6 Mar 2012 15:54:27 -0800 Subject: Added alternate test for SI-5545. --- test/files/pos/t5545/S_1.scala | 4 ++++ test/files/pos/t5545/S_2.scala | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 test/files/pos/t5545/S_1.scala create mode 100644 test/files/pos/t5545/S_2.scala (limited to 'test/files/pos') diff --git a/test/files/pos/t5545/S_1.scala b/test/files/pos/t5545/S_1.scala new file mode 100644 index 0000000000..59ec1fd851 --- /dev/null +++ b/test/files/pos/t5545/S_1.scala @@ -0,0 +1,4 @@ +trait F[@specialized(Int) T1, R] { + def f(v1: T1): R + def g = v1 => f(v1) +} diff --git a/test/files/pos/t5545/S_2.scala b/test/files/pos/t5545/S_2.scala new file mode 100644 index 0000000000..59ec1fd851 --- /dev/null +++ b/test/files/pos/t5545/S_2.scala @@ -0,0 +1,4 @@ +trait F[@specialized(Int) T1, R] { + def f(v1: T1): R + def g = v1 => f(v1) +} -- cgit v1.2.3