summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@epfl.ch>2012-07-05 11:12:46 +0200
committerLukas Rytz <lukas.rytz@epfl.ch>2012-07-05 16:23:10 +0200
commit6c7f2b6460de1aa6d15a6005921ca50e98a54027 (patch)
tree0f18962abb2659e341af3669aba1c544aad88d13 /src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
parent33936243bdf597e438de8d10ae7b3ed30454be9f (diff)
downloadscala-6c7f2b6460de1aa6d15a6005921ca50e98a54027.tar.gz
scala-6c7f2b6460de1aa6d15a6005921ca50e98a54027.tar.bz2
scala-6c7f2b6460de1aa6d15a6005921ca50e98a54027.zip
SI-5907, SI-5009 case-class copy defaults only for first param list
`copy` no longer returns anonymous functions if there are multiple parameter lists, reverts most of 40e7cab7a2. Cleaned up the special type completer for copy methods.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Unapplies.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Unapplies.scala73
1 files changed, 46 insertions, 27 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
index 4c20d14406..1b89f3db44 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
@@ -197,42 +197,61 @@ trait Unapplies extends ast.TreeDSL
)
}
+ /**
+ * Generates copy methods for case classes. Copy only has defaults on the first
+ * parameter list, as of SI-5009.
+ *
+ * The parameter types of the copy method need to be exactly the same as the parameter
+ * types of the primary constructor. Just copying the TypeTree is not enough: a type `C`
+ * might refer to something else *inside* the class (i.e. as parameter type of `copy`)
+ * than *outside* the class (i.e. in the class parameter list).
+ *
+ * One such example is t0054.scala:
+ * class A {
+ * case class B(x: C) extends A { def copy(x: C = x) = ... }
+ * class C {} ^ ^
+ * } (1) (2)
+ *
+ * The reference (1) to C is `A.this.C`. The reference (2) is `B.this.C` - not the same.
+ *
+ * This is fixed with a hack currently. `Unapplies.caseClassCopyMeth`, which creates the
+ * copy method, uses empty `TypeTree()` nodes for parameter types.
+ *
+ * In `Namers.enterDefDef`, the copy method gets a special type completer (`enterCopyMethod`).
+ * Before computing the body type of `copy`, the class parameter types are assigned the copy
+ * method parameters.
+ *
+ * This attachment class stores the copy method parameter ValDefs as an attachment in the
+ * ClassDef of the case class.
+ */
def caseClassCopyMeth(cdef: ClassDef): Option[DefDef] = {
def isDisallowed(vd: ValDef) = isRepeatedParamType(vd.tpt) || isByNameParamType(vd.tpt)
- val cparamss = constrParamss(cdef)
- val flat = cparamss.flatten
+ val classParamss = constrParamss(cdef)
- if (cdef.symbol.hasAbstractFlag || (flat exists isDisallowed)) None
+ if (cdef.symbol.hasAbstractFlag || mexists(classParamss)(isDisallowed)) None
else {
+ def makeCopyParam(vd: ValDef, putDefault: Boolean) = {
+ val rhs = if (putDefault) toIdent(vd) else EmptyTree
+ val flags = PARAM | (vd.mods.flags & IMPLICIT) | (if (putDefault) DEFAULTPARAM else 0)
+ // empty tpt: see comment above
+ val tpt = atPos(vd.pos.focus)(TypeTree() setOriginal vd.tpt)
+ treeCopy.ValDef(vd, Modifiers(flags), vd.name, tpt, rhs)
+ }
+
val tparams = cdef.tparams map copyUntypedInvariant
- // the parameter types have to be exactly the same as the constructor's parameter types; so it's
- // not good enough to just duplicated the (untyped) tpt tree; the parameter types are removed here
- // and re-added in ``finishWith'' in the namer.
- def paramWithDefault(vd: ValDef) =
- treeCopy.ValDef(vd, vd.mods | DEFAULTPARAM, vd.name, atPos(vd.pos.focus)(TypeTree() setOriginal vd.tpt), toIdent(vd))
-
- val (copyParamss, funParamss) = cparamss match {
- case Nil => (Nil, Nil)
+ val paramss = classParamss match {
+ case Nil => Nil
case ps :: pss =>
- (List(ps.map(paramWithDefault)), mmap(pss)(p => copyUntyped[ValDef](p).copy(rhs = EmptyTree)))
+ ps.map(makeCopyParam(_, putDefault = true)) :: mmap(pss)(makeCopyParam(_, putDefault = false))
}
val classTpe = classType(cdef, tparams)
- val bodyTpe = funParamss.foldRight(classTpe)((params, restp) => gen.scalaFunctionConstr(params.map(_.tpt), restp))
-
- val argss = copyParamss match {
- case Nil => Nil
- case ps :: _ => mmap(ps :: funParamss)(toIdent)
- }
- def mkFunction(vparams: List[ValDef], body: Tree) = Function(vparams, body)
- val body = funParamss.foldRight(New(classTpe, argss): Tree)(mkFunction)
- // [Eugene++] no longer compiles after I moved the `Function` case class into scala.reflect.internal
- // val body = funParamss.foldRight(New(classTpe, argss): Tree)(Function)
-
- Some(atPos(cdef.pos.focus)(
- DefDef(Modifiers(SYNTHETIC), nme.copy, tparams, copyParamss, bodyTpe,
- body)
- ))
+ val argss = mmap(paramss)(toIdent)
+ val body: Tree = New(classTpe, argss)
+ val copyDefDef = atPos(cdef.pos.focus)(
+ DefDef(Modifiers(SYNTHETIC), nme.copy, tparams, paramss, TypeTree(), body)
+ )
+ Some(copyDefDef)
}
}
}