summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala52
1 files changed, 44 insertions, 8 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index cc3d980cf1..17e67e6429 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -1,5 +1,5 @@
/* NSC -- new Scala compiler
- * Copyright 2005-2012 LAMP/EPFL
+ * Copyright 2005-2013 LAMP/EPFL
* @author Martin Odersky
*/
@@ -36,11 +36,31 @@ trait SyntheticMethods extends ast.TreeDSL {
import definitions._
import CODE._
+ private lazy val productSymbols = List(Product_productPrefix, Product_productArity, Product_productElement, Product_iterator, Product_canEqual)
+ private lazy val valueSymbols = List(Any_hashCode, Any_equals)
+ private lazy val caseSymbols = List(Object_hashCode, Object_toString) ::: productSymbols
+ private lazy val caseValueSymbols = Any_toString :: valueSymbols ::: productSymbols
+ private lazy val caseObjectSymbols = Object_equals :: caseSymbols
+ private def symbolsToSynthesize(clazz: Symbol): List[Symbol] = {
+ if (clazz.isCase) {
+ if (clazz.isDerivedValueClass) caseValueSymbols
+ else if (clazz.isModuleClass) caseSymbols
+ else caseObjectSymbols
+ }
+ else if (clazz.isDerivedValueClass) valueSymbols
+ else Nil
+ }
+
/** Add the synthetic methods to case classes.
*/
def addSyntheticMethods(templ: Template, clazz0: Symbol, context: Context): Template = {
-
- if (phase.erasedTypes)
+ val syntheticsOk = (phase.id <= currentRun.typerPhase.id) && {
+ symbolsToSynthesize(clazz0) filter (_ matchingSymbol clazz0.info isSynthetic) match {
+ case Nil => true
+ case syms => log("Not adding synthetic methods: already has " + syms.mkString(", ")) ; false
+ }
+ }
+ if (!syntheticsOk)
return templ
val synthesizer = new ClassMethodSynthesis(
@@ -94,9 +114,9 @@ trait SyntheticMethods extends ast.TreeDSL {
Apply(gen.mkAttributedRef(method), args.toList)
}
- // Any member, including private
+ // Any concrete member, including private
def hasConcreteImpl(name: Name) =
- clazz.info.member(name).alternatives exists (m => !m.isDeferred && !m.isSynthetic)
+ clazz.info.member(name).alternatives exists (m => !m.isDeferred)
def hasOverridingImplementation(meth: Symbol) = {
val sym = clazz.info nonPrivateMember meth.name
@@ -306,7 +326,24 @@ trait SyntheticMethods extends ast.TreeDSL {
else Nil
)
- def impls = for ((m, impl) <- methods ; if !hasOverridingImplementation(m)) yield impl()
+ /** Always generate overrides for equals and hashCode in value classes,
+ * so they can appear in universal traits without breaking value semantics.
+ */
+ def impls = {
+ def shouldGenerate(m: Symbol) = {
+ !hasOverridingImplementation(m) || {
+ clazz.isDerivedValueClass && (m == Any_hashCode || m == Any_equals) && {
+ if (settings.lint.value) {
+ (clazz.info nonPrivateMember m.name) filter (m => (m.owner != AnyClass) && (m.owner != clazz) && !m.isDeferred) andAlso { m =>
+ currentUnit.warning(clazz.pos, s"Implementation of ${m.name} inherited from ${m.owner} overridden in $clazz to enforce value class semantics")
+ }
+ }
+ true
+ }
+ }
+ }
+ for ((m, impl) <- methods ; if shouldGenerate(m)) yield impl()
+ }
def extras = (
if (needsReadResolve) {
// Aha, I finally decoded the original comment.
@@ -347,8 +384,7 @@ trait SyntheticMethods extends ast.TreeDSL {
(lb ++= templ.body ++= synthesize()).toList
}
- if (phase.id > currentRun.typerPhase.id) templ
- else deriveTemplate(templ)(body =>
+ deriveTemplate(templ)(body =>
if (clazz.isCase) caseTemplateBody()
else synthesize() match {
case Nil => body // avoiding unnecessary copy