summaryrefslogtreecommitdiff
path: root/src/compiler/scala
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-09-26 23:49:03 +0000
committerPaul Phillips <paulp@improving.org>2011-09-26 23:49:03 +0000
commite412524fee20da975954c929893e88db17dbdd9a (patch)
treee7c491b8d303879b49426d98f714d81c672d885f /src/compiler/scala
parent660d80f682317eaf3d55a9b63bb95396ac417cdd (diff)
downloadscala-e412524fee20da975954c929893e88db17dbdd9a.tar.gz
scala-e412524fee20da975954c929893e88db17dbdd9a.tar.bz2
scala-e412524fee20da975954c929893e88db17dbdd9a.zip
ProductN, and method synthesis toolbox.
- Finished giving case classes a ProductN parent, and flipped it on. The "finish" part involved not breaking existing code where case classes manually extend the appropriate ProductN. (Like, Tuple 1-22.) - Generalized most of SyntheticMethods to ease method creation and class manipulation in general. - Fixed bugs related to the above, like the fact that this used to be a compile error: scala> case class Foo() extends Serializable <console>:28: error: trait Serializable is inherited twice case class Foo() extends Serializable ^ It feels like there's a better way to eliminate the duplicate parents, but after spending a lot of time chasing my tail in that peril-fraught zone between namer and typer, I don't see an easy path to improve on it. Closes SI-1799. For that modification to Typers, review by odersky.
Diffstat (limited to 'src/compiler/scala')
-rw-r--r--src/compiler/scala/reflect/internal/Definitions.scala27
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/matching/ParallelMatching.scala22
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala480
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala22
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Unapplies.scala10
7 files changed, 332 insertions, 233 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala
index 2c51ca0db5..17dc33278d 100644
--- a/src/compiler/scala/reflect/internal/Definitions.scala
+++ b/src/compiler/scala/reflect/internal/Definitions.scala
@@ -420,6 +420,7 @@ trait Definitions extends reflect.api.StandardDefinitions {
lazy val ProductClass = mkArityArray("Product", MaxProductArity)
lazy val FunctionClass = mkArityArray("Function", MaxFunctionArity, 0)
lazy val AbstractFunctionClass = mkArityArray("runtime.AbstractFunction", MaxFunctionArity, 0)
+ lazy val isProductNClass = ProductClass.toSet
def tupleField(n: Int, j: Int) = getMember(TupleClass(n), "_" + j)
def isTupleType(tp: Type): Boolean = isTupleType(tp, false)
@@ -454,11 +455,7 @@ trait Definitions extends reflect.api.StandardDefinitions {
def productProj(n: Int, j: Int): Symbol = productProj(ProductClass(n), j)
/** returns true if this type is exactly ProductN[T1,...,Tn], not some subclass */
- def isExactProductType(tp: Type): Boolean = cond(tp.normalize) {
- case TypeRef(_, sym, elems) =>
- val len = elems.length
- len <= MaxProductArity && sym == ProductClass(len)
- }
+ def isExactProductType(tp: Type): Boolean = isProductNClass(tp.typeSymbol)
def productType(elems: List[Type]) = {
if (elems.isEmpty) UnitClass.tpe
@@ -472,15 +469,16 @@ trait Definitions extends reflect.api.StandardDefinitions {
}
}
- /** if tpe <: ProductN[T1,...,TN], returns Some((T1,...,TN)) else None */
- def getProductArgs(tpe: Type): Option[List[Type]] =
- tpe.baseClasses collectFirst { case x if isExactProductType(x.tpe) => tpe.baseType(x).typeArgs }
+ /** if tpe <: ProductN[T1,...,TN], returns List(T1,...,TN) else Nil */
+ def getProductArgs(tpe: Type): List[Type] = tpe.baseClasses find isProductNClass match {
+ case Some(x) => tpe.baseType(x).typeArgs
+ case _ => Nil
+ }
- def unapplyUnwrap(tpe:Type) = (tpe match {
- case PolyType(_,MethodType(_, res)) => res
- case MethodType(_, res) => res
- case tpe => tpe
- }).normalize
+ def unapplyUnwrap(tpe:Type) = tpe.finalResultType.normalize match {
+ case RefinedType(p :: _, _) => p.normalize
+ case tp => tp
+ }
def functionApply(n: Int) = getMember(FunctionClass(n), nme.apply)
def functionType(formals: List[Type], restpe: Type) = {
@@ -753,6 +751,9 @@ trait Definitions extends reflect.api.StandardDefinitions {
/** Is symbol a phantom class for which no runtime representation exists? */
lazy val isPhantomClass = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)
+ /** Is the symbol that of a parent which is added during parsing? */
+ lazy val isPossibleSyntheticParent = ProductClass.toSet[Symbol] + ProductRootClass + SerializableClass
+
private lazy val scalaValueClassesSet = ScalaValueClasses.toSet
private lazy val boxedValueClassesSet = boxedClass.values.toSet + BoxedUnitClass
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 17688d42d8..615efb986a 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -2675,7 +2675,7 @@ self =>
def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = {
/** A synthetic ProductN parent for case classes. */
def extraCaseParents = (
- if (settings.Xexperimental.value && mods.isCase) {
+ if (mods.isCase) {
val arity = if (vparamss.isEmpty || vparamss.head.isEmpty) 0 else vparamss.head.size
if (arity == 0) Nil
else List(
diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
index dc3328de70..197839c7e0 100644
--- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
+++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
@@ -24,7 +24,7 @@ trait ParallelMatching extends ast.TreeDSL
self: ExplicitOuter =>
import global.{ typer => _, _ }
- import definitions.{ AnyRefClass, NothingClass, IntClass, BooleanClass, SomeClass, getProductArgs, productProj }
+ import definitions.{ AnyRefClass, NothingClass, IntClass, BooleanClass, SomeClass, OptionClass, getProductArgs, productProj }
import CODE._
import Types._
import Debug._
@@ -355,24 +355,26 @@ trait ParallelMatching extends ast.TreeDSL
lazy val unapplyResult: PatternVar =
scrut.createVar(unMethod.tpe, Apply(unTarget, scrut.id :: trailing) setType _.tpe)
- lazy val cond: Tree =
- if (unapplyResult.tpe.isBoolean) unapplyResult.ident
- else if (unapplyResult.tpe.typeSymbol == SomeClass) TRUE
- else NOT(unapplyResult.ident DOT nme.isEmpty)
+ lazy val cond: Tree = unapplyResult.tpe.normalize match {
+ case TypeRef(_, BooleanClass, _) => unapplyResult.ident
+ case TypeRef(_, SomeClass, _) => TRUE
+ case _ => NOT(unapplyResult.ident DOT nme.isEmpty)
+ }
lazy val failure =
mkFail(zipped.tail filterNot (x => SameUnapplyPattern(x._1)) map { case (pat, r) => r insert pat })
private def doSuccess: (List[PatternVar], List[PatternVar], List[Row]) = {
// pattern variable for the unapply result of Some(x).get
- lazy val pv = scrut.createVar(
- unMethod.tpe typeArgs 0,
- _ => fn(ID(unapplyResult.lhs), nme.get)
- )
+ def unMethodTypeArg = unMethod.tpe.baseType(OptionClass).typeArgs match {
+ case Nil => log("No type argument for unapply result! " + unMethod.tpe) ; NoType
+ case arg :: _ => arg
+ }
+ lazy val pv = scrut.createVar(unMethodTypeArg, _ => fn(ID(unapplyResult.lhs), nme.get))
def tuple = pv.lhs
// at this point it's Some[T1,T2...]
- lazy val tpes = getProductArgs(tuple.tpe).get
+ lazy val tpes = getProductArgs(tuple.tpe)
// one pattern variable per tuple element
lazy val tuplePVs =
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index d4565c8e0c..89ec052556 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -287,6 +287,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
infoStringWithLocation(other),
infoStringWithLocation(member)
)
+ else if (settings.debug.value)
+ analyzer.foundReqMsg(member.tpe, other.tpe)
else ""
"overriding %s;\n %s %s%s".format(
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index 9a8bcca2ba..2cc2e2c4aa 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -32,132 +32,230 @@ import scala.collection.mutable.ListBuffer
trait SyntheticMethods extends ast.TreeDSL {
self: Analyzer =>
- import global._ // the global environment
- import definitions._ // standard classes and methods
+ import global._
+ import definitions._
+ import CODE._
- /** Add the synthetic methods to case classes.
- */
- def addSyntheticMethods(templ: Template, clazz: Symbol, context: Context): Template = {
- import CODE._
+ private object util {
+ private type CM[T] = ClassManifest[T]
- val localTyper = newTyper(
- if (reporter.hasErrors) context makeSilent false else context
- )
- def accessorTypes = clazz.caseFieldAccessors map (_.tpe.finalResultType)
- def accessorLub = global.weakLub(accessorTypes)._1
+ /** To avoid unchecked warnings on polymorphic classes.
+ */
+ def clazzTypeToTest(clazz: Symbol) = clazz.tpe.normalize match {
+ case TypeRef(_, sym, args) if args.nonEmpty => ExistentialType(sym.typeParams, clazz.tpe)
+ case tp => tp
+ }
- def hasOverridingImplementation(meth: Symbol): Boolean = {
- val sym = clazz.info nonPrivateMember meth.name
- def isOverride(s: Symbol) = {
- s != meth && !s.isDeferred && !s.isSynthetic &&
- (clazz.thisType.memberType(s) matches clazz.thisType.memberType(meth))
- }
- sym.alternatives exists isOverride
+ def makeMethodPublic(method: Symbol): Symbol = {
+ method.privateWithin = NoSymbol
+ method resetFlag AccessFlags
}
- def syntheticMethod(name: Name, flags: Int, tpeCons: Symbol => Type) =
- newSyntheticMethod(name, flags | OVERRIDE, tpeCons)
+ def methodArg(method: Symbol, idx: Int): Tree = Ident(method.paramss.head(idx))
+
+ private def applyTypeInternal(manifests: List[CM[_]]): Type = {
+ val symbols = manifests map manifestToSymbol
+ val container :: args = symbols
+ val tparams = container.typeConstructor.typeParams
- def newSyntheticMethod(name: Name, flags: Int, tpeCons: Symbol => Type) = {
- val method = clazz.newMethod(clazz.pos.focus, name.toTermName) setFlag flags
+ // Overly conservative at present - if manifests were more usable
+ // this could do a lot more.
+ require(symbols forall (_ ne NoSymbol), "Must find all manifests: " + symbols)
+ require(container.owner.isPackageClass, "Container must be a top-level class in a package: " + container)
+ require(tparams.size == args.size, "Arguments must match type constructor arity: " + tparams + ", " + args)
+ require(args forall (_.typeConstructor.typeParams.isEmpty), "Arguments must be unparameterized: " + args)
- clazz.info.decls enter (method setInfo tpeCons(method))
+ typeRef(container.typeConstructor.prefix, container, args map (_.tpe))
}
- def newTypedMethod(method: Symbol)(body: Tree): Tree =
- localTyper typed { DEF(method) === body }
+ def manifestToSymbol(m: CM[_]): Symbol = m match {
+ case x: scala.reflect.AnyValManifest[_] => definitions.getClass("scala." + x)
+ case _ => getClassIfDefined(m.erasure.getName)
+ }
+ def companionType[T](implicit m: CM[T]) =
+ getModule(m.erasure.getName).tpe
+
+ // Use these like `applyType[List, Int]` or `applyType[Map, Int, String]`
+ def applyType[M](implicit m1: CM[M]): Type =
+ applyTypeInternal(List(m1))
- def makeNoArgConstructor(res: Type) =
- (sym: Symbol) => MethodType(Nil, res)
- def makeTypeConstructor(args: List[Type], res: Type) =
- (sym: Symbol) => MethodType(sym newSyntheticValueParams args, res)
- def makeEqualityMethod(name: Name) =
- syntheticMethod(name, 0, makeTypeConstructor(List(AnyClass.tpe), BooleanClass.tpe))
+ def applyType[M[X1], X1](implicit m1: CM[M[_]], m2: CM[X1]): Type =
+ applyTypeInternal(List(m1, m2))
- def newNullaryMethod(name: TermName, tpe: Type, body: Tree) = {
- val flags = if (clazz.tpe.member(name) != NoSymbol) OVERRIDE else 0
- val method = clazz.newMethod(clazz.pos.focus, name) setFlag flags
+ def applyType[M[X1, X2], X1, X2](implicit m1: CM[M[_,_]], m2: CM[X1], m3: CM[X2]): Type =
+ applyTypeInternal(List(m1, m2, m3))
- clazz.info.decls enter (method setInfo NullaryMethodType(tpe))
- newTypedMethod(method)(body)
+ def applyType[M[X1, X2, X3], X1, X2, X3](implicit m1: CM[M[_,_,_]], m2: CM[X1], m3: CM[X2], m4: CM[X3]): Type =
+ applyTypeInternal(List(m1, m2, m3, m4))
+ }
+ import util._
+
+ class MethodSynthesis(val clazz: Symbol, localTyper: Typer) {
+ private def isOverride(method: Symbol) =
+ clazzMember(method.name).alternatives exists (sym => (sym != method) && !sym.isDeferred)
+
+ private def setMethodFlags(method: Symbol): Symbol = {
+ val overrideFlag = if (isOverride(method)) OVERRIDE else 0L
+
+ method setFlag (overrideFlag | SYNTHETIC) resetFlag DEFERRED
}
- def productPrefixMethod = newNullaryMethod(nme.productPrefix, StringClass.tpe, LIT(clazz.name.decode))
- def productArityMethod(arity: Int) = newNullaryMethod(nme.productArity, IntClass.tpe, LIT(arity))
- def productIteratorMethod = {
- val method = getMember(ScalaRunTimeModule, "typedProductIterator")
- val iteratorType = typeRef(NoPrefix, IteratorClass, List(accessorLub))
- newNullaryMethod(
- nme.productIterator,
- iteratorType,
- gen.mkMethodCall(method, List(accessorLub), List(This(clazz)))
- )
+ private def finishMethod(method: Symbol, f: Symbol => Tree): Tree = {
+ setMethodFlags(method)
+ clazz.info.decls enter method
+ logResult("finishMethod")(localTyper typed DefDef(method, f(method)))
}
- def projectionMethod(accessor: Symbol, num: Int) = {
- newNullaryMethod(nme.productAccessorName(num), accessor.tpe.resultType, REF(accessor))
+ private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = {
+ val m = clazz.newMethod(clazz.pos.focus, name.toTermName)
+ m setInfo info
+ finishMethod(m, f)
+ }
+ private def createInternal(name: Name, f: Symbol => Tree, infoFn: Symbol => Type): Tree = {
+ val m = clazz.newMethod(clazz.pos.focus, name.toTermName)
+ m setInfo infoFn(m)
+ finishMethod(m, f)
}
- /** Common code for productElement and (currently disabled) productElementName
- */
- def perElementMethod(accs: List[Symbol], methodName: Name, resType: Type, caseFn: Symbol => Tree): Tree = {
- val symToTpe = makeTypeConstructor(List(IntClass.tpe), resType)
- val method = syntheticMethod(methodName, 0, symToTpe)
- val arg = method ARG 0
- val default = List(DEFAULT ==> THROW(IndexOutOfBoundsExceptionClass, arg))
- val cases =
- for ((sym, i) <- accs.zipWithIndex) yield
- CASE(LIT(i)) ==> caseFn(sym)
-
- newTypedMethod(method)(arg MATCH { cases ::: default : _* })
+ private def cloneInternal(original: Symbol, f: Symbol => Tree, name: Name): Tree = {
+ val m = original.cloneSymbol(clazz) setPos clazz.pos.focus
+ m.name = name
+ finishMethod(m, f)
+ }
+
+ private def cloneInternal(original: Symbol, f: Symbol => Tree): Tree =
+ cloneInternal(original, f, original.name)
+
+ def clazzMember(name: Name) = clazz.info nonPrivateMember name match {
+ case NoSymbol => log("In " + clazz + ", " + name + " not found: " + clazz.info) ; NoSymbol
+ case sym => sym
}
- def productElementMethod(accs: List[Symbol]): Tree =
- perElementMethod(accs, nme.productElement, AnyClass.tpe, x => Ident(x))
+ def typeInClazz(sym: Symbol) = clazz.thisType memberType sym
+
+ /** Function argument takes the newly created method symbol of
+ * the same type as `name` in clazz, and returns the tree to be
+ * added to the template.
+ */
+ def overrideMethod(name: Name)(f: Symbol => Tree): Tree =
+ overrideMethod(clazzMember(name))(f)
+
+ def overrideMethod(original: Symbol)(f: Symbol => Tree): Tree =
+ cloneInternal(original, sym => f(sym setFlag OVERRIDE))
+
+ def deriveMethod(original: Symbol, nameFn: Name => Name)(f: Symbol => Tree): Tree =
+ cloneInternal(original, f, nameFn(original.name))
- // def productElementNameMethod(accs: List[Symbol]): Tree =
- // perElementMethod(accs, nme.productElementName, StringClass.tpe, x => Literal(x.name.toString))
+ def createMethod(name: Name, paramTypes: List[Type], returnType: Type)(f: Symbol => Tree): Tree =
+ createInternal(name, f, (m: Symbol) => MethodType(m newSyntheticValueParams paramTypes, returnType))
- def moduleToStringMethod: Tree = {
- val method = syntheticMethod(nme.toString_, FINAL, makeNoArgConstructor(StringClass.tpe))
- newTypedMethod(method)(LIT(clazz.name.decode))
+ def createMethod(name: Name, returnType: Type)(f: Symbol => Tree): Tree =
+ createInternal(name, f, NullaryMethodType(returnType))
+
+ def createMethod(original: Symbol)(f: Symbol => Tree): Tree =
+ createInternal(original.name, f, original.info)
+
+ def forwardMethod(original: Symbol, newMethod: Symbol)(transformArgs: List[Tree] => List[Tree]): Tree =
+ createMethod(original)(m => gen.mkMethodCall(newMethod, transformArgs(m.paramss.head map Ident)))
+
+ def createSwitchMethod(name: Name, range: Seq[Int], returnType: Type)(f: Int => Tree) = {
+ createMethod(name, List(IntClass.tpe), returnType) { m =>
+ val arg0 = methodArg(m, 0)
+ val default = DEFAULT ==> THROW(IndexOutOfBoundsExceptionClass, arg0)
+ val cases = range.map(num => CASE(LIT(num)) ==> f(num)).toList :+ default
+
+ Match(arg0, cases)
+ }
}
- def moduleHashCodeMethod: Tree = {
- val method = syntheticMethod(nme.hashCode_, FINAL, makeNoArgConstructor(IntClass.tpe))
- // The string being used as hashcode basis is also productPrefix.
- newTypedMethod(method)(LIT(clazz.name.decode.hashCode))
+
+ // def foo() = constant
+ def constantMethod(name: Name, value: Any): Tree = {
+ val constant = Constant(value)
+ createMethod(name, Nil, constant.tpe)(_ => Literal(constant))
}
+ // def foo = constant
+ def constantNullary(name: Name, value: Any): Tree = {
+ val constant = Constant(value)
+ createMethod(name, constant.tpe)(_ => Literal(constant))
+ }
+ }
+
+ /** Add the synthetic methods to case classes.
+ */
+ def addSyntheticMethods(templ: Template, clazz0: Symbol, context: Context): Template = {
+ if (phase.erasedTypes)
+ return templ
- def forwardingMethod(name: Name, targetName: Name): Tree = {
- val target = getMember(ScalaRunTimeModule, targetName)
- val paramtypes = target.tpe.paramTypes drop 1
- val method = syntheticMethod(name, 0, makeTypeConstructor(paramtypes, target.tpe.resultType))
+ val synthesizer = new MethodSynthesis(
+ clazz0,
+ newTyper( if (reporter.hasErrors) context makeSilent false else context )
+ )
+ import synthesizer._
- newTypedMethod(method)(Apply(REF(target), This(clazz) :: (method ARGNAMES)))
+ def accessors = clazz.caseFieldAccessors
+ def arity = accessors.size
+
+ def forwardToRuntime(method: Symbol): Tree =
+ forwardMethod(method, getMember(ScalaRunTimeModule, "_" + method.name toTermName))(This(clazz) :: _)
+
+ // Any member, including private
+ def hasConcreteImpl(name: Name) =
+ clazz.info.member(name).alternatives exists (m => !m.isDeferred && !m.isSynthetic)
+
+ 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))
+ }
}
- /** The equality method for case modules:
- * def equals(that: Any) = this eq that
- */
- def equalsModuleMethod: Tree = {
- val method = makeEqualityMethod(nme.equals_)
+ def productIteratorMethod = {
+ // If this is ProductN[T1, T2, ...], accessorLub is the lub of T1, T2, ..., .
+ val accessorLub = (
+ if (opt.experimental)
+ global.weakLub(accessors map (_.tpe.finalResultType))._1 match {
+ case RefinedType(parents, decls) if !decls.isEmpty => RefinedType(parents, EmptyScope)
+ case tp => tp
+ }
+ else AnyClass.tpe
+ )
+ // !!! Hidden behind -Xexperimental due to bummer type inference bugs.
+ // Refining from Iterator[Any] leads to types like
+ //
+ // Option[Int] { def productIterator: Iterator[String] }
+ //
+ // appearing legitimately, but this breaks invariant places
+ // like Manifests and Arrays which are not robust and infer things
+ // which they shouldn't.
+ val iteratorType = typeRef(NoPrefix, IteratorClass, List(accessorLub))
- newTypedMethod(method)(This(clazz) ANY_EQ (method ARG 0))
+ createMethod(nme.productIterator, iteratorType)(_ =>
+ gen.mkMethodCall(ScalaRunTimeModule, "typedProductIterator", List(accessorLub), List(This(clazz)))
+ )
+ }
+ def projectionMethod(accessor: Symbol, num: Int) = {
+ createMethod(nme.productAccessorName(num), accessor.tpe.resultType)(_ => REF(accessor))
}
- /** To avoid unchecked warnings on polymorphic classes.
+ /** Common code for productElement and (currently disabled) productElementName
*/
- def clazzTypeToTest = clazz.tpe.normalize match {
- case TypeRef(pre, sym, args) if args.nonEmpty => ExistentialType(sym.typeParams, clazz.tpe)
- case tp => tp
- }
+ def perElementMethod(name: Name, returnType: Type)(caseFn: Symbol => Tree): Tree =
+ createSwitchMethod(name, accessors.indices, returnType)(idx => caseFn(accessors(idx)))
+
+ def productElementNameMethod = perElementMethod(nme.productElement, StringClass.tpe)(x => LIT(x.name.toString))
+
+ /** The equality method for case modules:
+ * def equals(that: Any) = this eq that
+ */
+ def equalsModuleMethod: Tree =
+ createMethod(Object_equals)(m => This(clazz) ANY_EQ methodArg(m, 0))
/** The canEqual method for case classes.
* def canEqual(that: Any) = that.isInstanceOf[This]
*/
def canEqualMethod: Tree = {
- val method = makeEqualityMethod(nme.canEqual_)
-
- newTypedMethod(method)((method ARG 0) IS_OBJ clazzTypeToTest)
+ createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m =>
+ methodArg(m, 0) IS_OBJ clazzTypeToTest(clazz)
+ )
}
/** The equality method for case classes.
@@ -171,145 +269,123 @@ trait SyntheticMethods extends ast.TreeDSL {
* }
* }
*/
- def equalsClassMethod: Tree = {
- val method = makeEqualityMethod(nme.equals_)
- val that = Ident(method.paramss.head.head)
- val typeTest = gen.mkIsInstanceOf(that, clazzTypeToTest, true, false)
- val typeCast = that AS_ATTR clazz.tpe
-
- def argsBody = {
- val valName = context.unit.freshTermName(clazz.name + "$")
- val valSym = method.newValue(method.pos, valName) setInfo clazz.tpe setFlag SYNTHETIC
- val valDef = ValDef(valSym, typeCast)
- val canEqualCheck = gen.mkMethodCall(valSym, nme.canEqual_, Nil, List(This(clazz)))
- val pairwise = clazz.caseFieldAccessors map { accessor =>
- val method = accessor.tpe member nme.EQ
- fn(Select(This(clazz), accessor), method, Select(Ident(valSym), accessor))
+ def equalsClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m =>
+ val arg0 = methodArg(m, 0)
+ val thatTest = gen.mkIsInstanceOf(arg0, clazzTypeToTest(clazz), true, false)
+ val thatCast = arg0 AS_ATTR clazz.tpe
+
+ def argsBody: Tree = {
+ val otherName = context.unit.freshTermName(clazz.name + "$")
+ val otherSym = m.newValue(m.pos, otherName) setInfo clazz.tpe setFlag SYNTHETIC
+ 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: _*)
+ )
}
+ }
+ if (accessors.isEmpty)
+ thatTest AND ((thatCast DOT nme.canEqual_)(This(clazz)))
+ else
+ argsBody
+ }
- (
- (This(clazz) ANY_EQ that) OR
- (typeTest AND Block(valDef, AND(pairwise :+ canEqualCheck: _*)))
- )
+ def newAccessorMethod(ddef: DefDef): Tree = {
+ deriveMethod(ddef.symbol, name => context.unit.freshTermName(name + "$")) { newAcc =>
+ makeMethodPublic(newAcc)
+ newAcc resetFlag (ACCESSOR | PARAMACCESSOR)
+ ddef.rhs.duplicate
}
+ }
+
+ // A buffer collecting additional methods for the template body
+ val ts = new ListBuffer[Tree]
- newTypedMethod(method) {
- if (clazz.caseFieldAccessors.isEmpty)
- typeTest AND ((typeCast DOT nme.canEqual_)(This(clazz)))
- else
- argsBody
+ def makeAccessorsPublic() {
+ // If this case class has fields with less than public visibility, their getter at this
+ // point also has those permissions. In that case we create a new, public accessor method
+ // with a new name and remove the CASEACCESSOR flag from the existing getter. This complicates
+ // the retrieval of the case field accessors (see def caseFieldAccessors in Symbols.)
+ def needsService(s: Symbol) = s.isMethod && s.isCaseAccessor && !s.isPublic
+ for (ddef @ DefDef(_, _, _, _, _, _) <- templ.body; if needsService(ddef.symbol)) {
+ ts += newAccessorMethod(ddef)
+ ddef.symbol resetFlag CASEACCESSOR
}
}
+ /** The _1, _2, etc. methods to implement ProductN.
+ */
+ def productNMethods = {
+ val accs = accessors.toIndexedSeq
+ 1 to arity map (num => productProj(arity, num) -> (() => projectionMethod(accs(num - 1), num)))
+ }
- def newAccessorMethod(tree: Tree): Tree = tree match {
- case DefDef(_, _, _, _, _, rhs) =>
- val newAcc = tree.symbol.cloneSymbol
- newAcc.name = context.unit.freshTermName(tree.symbol.name + "$")
- newAcc setFlag SYNTHETIC resetFlag (ACCESSOR | PARAMACCESSOR | PRIVATE | PROTECTED)
- newAcc.privateWithin = NoSymbol
- newAcc.owner.info.decls enter newAcc
- logResult("new accessor method")(newTypedMethod(newAcc)(rhs.duplicate))
+ // methods for both classes and objects
+ def productMethods = {
+ List(
+ Product_productPrefix -> (() => constantNullary(nme.productPrefix, clazz.name.decode)),
+ Product_productArity -> (() => constantNullary(nme.productArity, arity)),
+ Product_productElement -> (() => perElementMethod(nme.productElement, AnyClass.tpe)(Ident)),
+ Product_iterator -> (() => productIteratorMethod),
+ Product_canEqual -> (() => canEqualMethod)
+ // This is disabled pending a reimplementation which doesn't add any
+ // weight to case classes (i.e. inspects the bytecode.)
+ // Product_productElementName -> (() => productElementNameMethod(accessors)),
+ )
}
- def needsReadResolve = (
- // Only nested objects inside objects should get readResolve automatically.
- // Otherwise, after de-serialization we get null references for lazy accessors
- // (nested object -> lazy val + class def) since the bitmap gets serialized but
- // the moduleVar not.
- clazz.isSerializable && (clazz.owner.isPackageClass || clazz.owner.isModuleClass)
+ def caseClassMethods = productMethods ++ productNMethods ++ Seq(
+ Object_hashCode -> (() => forwardToRuntime(Object_hashCode)),
+ Object_toString -> (() => forwardToRuntime(Object_toString)),
+ Object_equals -> (() => equalsClassMethod)
)
- // A buffer collecting additional methods for the template body
- val ts = new ListBuffer[Tree]
+ def caseObjectMethods = productMethods ++ Seq(
+ Object_hashCode -> (() => constantMethod(nme.hashCode_, clazz.name.decode.hashCode)),
+ Object_toString -> (() => constantMethod(nme.toString_, clazz.name.decode))
+ )
- if (!phase.erasedTypes) try {
+ def synthesize() = {
if (clazz.isCase) {
- val isTop = clazz.ancestors forall (x => !x.isCase)
- val accessors = clazz.caseFieldAccessors
- val arity = accessors.size
-
- if (isTop) {
- // If this case class has fields with less than public visibility, their getter at this
- // point also has those permissions. In that case we create a new, public accessor method
- // with a new name and remove the CASEACCESSOR flag from the existing getter. This complicates
- // the retrieval of the case field accessors (see def caseFieldAccessors in Symbols.)
- def needsService(s: Symbol) = s.isMethod && s.isCaseAccessor && !s.isPublic
- for (stat <- templ.body ; if stat.isDef && needsService(stat.symbol)) {
- ts += newAccessorMethod(stat)
- stat.symbol resetFlag CASEACCESSOR
- }
- }
- /** The _1, _2, etc. methods to implement ProductN, and an override
- * of productIterator with a more specific element type.
- * Only enabled under -Xexperimental.
- */
- def productNMethods = {
- val projectionMethods = (accessors, 1 to arity).zipped map ((accessor, num) =>
- productProj(arity, num) -> (() => projectionMethod(accessor, num))
- )
- projectionMethods :+ (
- Product_iterator -> (() => productIteratorMethod)
- )
- }
+ makeAccessorsPublic()
+ val methods = if (clazz.isModuleClass) caseObjectMethods else caseClassMethods
- // methods for case classes only
- def classMethods = List(
- Object_hashCode -> (() => forwardingMethod(nme.hashCode_, "_" + nme.hashCode_)),
- Object_toString -> (() => forwardingMethod(nme.toString_, "_" + nme.toString_)),
- Object_equals -> (() => equalsClassMethod)
- ) ++ (
- if (settings.Xexperimental.value) productNMethods
- else Nil
- )
-
- // methods for case objects only
- def objectMethods = List(
- Object_hashCode -> (() => moduleHashCodeMethod),
- Object_toString -> (() => moduleToStringMethod)
- )
- // methods for both classes and objects
- def everywhereMethods = {
- List(
- Product_productPrefix -> (() => productPrefixMethod),
- Product_productArity -> (() => productArityMethod(accessors.length)),
- Product_productElement -> (() => productElementMethod(accessors)),
- // This is disabled pending a reimplementation which doesn't add any
- // weight to case classes (i.e. inspects the bytecode.)
- // Product_productElementName -> (() => productElementNameMethod(accessors)),
- Product_canEqual -> (() => canEqualMethod)
- )
- }
-
- val methods = (if (clazz.isModuleClass) objectMethods else classMethods) ++ everywhereMethods
for ((m, impl) <- methods ; if !hasOverridingImplementation(m))
ts += impl()
}
+ /** If you serialize a singleton and then deserialize it twice,
+ * you will have two instances of your singleton, unless you implement
+ * the readResolve() method (see http://www.javaworld.com/javaworld/
+ * jw-04-2003/jw-0425-designpatterns_p.html)
+ */
if (clazz.isModuleClass) {
- def hasReadResolve = {
- val sym = clazz.info member nme.readResolve // any member, including private
- sym.isTerm && !sym.isDeferred
- }
-
- /** If you serialize a singleton and then deserialize it twice,
- * you will have two instances of your singleton, unless you implement
- * the readResolve() method (see http://www.javaworld.com/javaworld/
- * jw-04-2003/jw-0425-designpatterns_p.html)
- */
- if (!hasReadResolve && needsReadResolve) {
+ // Only nested objects inside objects should get readResolve automatically.
+ // Otherwise, after de-serialization we get null references for lazy accessors
+ // (nested object -> lazy val + class def) since the bitmap gets serialized but
+ // the moduleVar not.
+ def needsReadResolve =
+ !hasConcreteImpl(nme.readResolve) && clazz.isSerializable && clazz.owner.isModuleClass
+
+ if (needsReadResolve) {
// PP: To this day I really can't figure out what this next comment is getting at:
// the !!! normally means there is something broken, but if so, what is it?
//
// !!! the synthetic method "readResolve" should be private, but then it is renamed !!!
- val method = newSyntheticMethod(nme.readResolve, PROTECTED, makeNoArgConstructor(ObjectClass.tpe))
- ts += newTypedMethod(method)(REF(clazz.sourceModule))
+ ts += createMethod(nme.readResolve, Nil, ObjectClass.tpe) { m =>
+ m setFlag PROTECTED
+ REF(clazz.sourceModule)
+ }
}
}
- } catch {
- case ex: TypeError =>
- if (!reporter.hasErrors) throw ex
}
+ try synthesize()
+ catch { case _: TypeError if reporter.hasErrors => () }
+
if (phase.id <= currentRun.typerPhase.id) {
treeCopy.Template(templ, templ.parents, templ.self,
if (ts.isEmpty) templ.body else templ.body ++ ts // avoid copying templ.body if empty
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index d1ba2df815..1c8b2486c9 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1213,8 +1213,26 @@ trait Typers extends Modes with Adaptations {
*/
//Console.println("parents("+clazz") = "+supertpt :: mixins);//DEBUG
- supertpt :: mixins mapConserve (tpt => checkNoEscaping.privates(clazz, tpt))
- } catch {
+
+ // Certain parents are added in the parser before it is known whether
+ // that class also declared them as parents. For instance, this is an
+ // error unless we take corrective action here:
+ //
+ // case class Foo() extends Serializable
+ //
+ // So we strip the duplicates before typer.
+ def fixDuplicates(remaining: List[Tree]): List[Tree] = remaining match {
+ case Nil => Nil
+ case x :: xs =>
+ val sym = x.symbol
+ x :: fixDuplicates(
+ if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym)
+ else xs
+ )
+ }
+ fixDuplicates(supertpt :: mixins) mapConserve (tpt => checkNoEscaping.privates(clazz, tpt))
+ }
+ catch {
case ex: TypeError =>
templ.tpe = null
reportTypeError(templ.pos, ex)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
index 67dd272d5a..4f7e6225e1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
@@ -42,11 +42,11 @@ trait Unapplies extends ast.TreeDSL
tp.typeSymbol match { // unapplySeqResultToMethodSig
case BooleanClass => Nil
case OptionClass | SomeClass =>
- val prod = tp.typeArgs.head
- getProductArgs(prod) match {
- case Some(xs) if xs.size > 1 => xs // n > 1
- case _ => List(prod) // special n == 0 || n == 1
- }
+ val prod = tp.typeArgs.head
+ val targs = getProductArgs(prod)
+
+ if (targs.isEmpty || targs.tail.isEmpty) List(prod) // special n == 0 || n == 1
+ else targs // n > 1
case _ =>
throw new TypeError("result type "+tp+" of unapply not in {Boolean, Option[_], Some[_]}")
}