summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2008-01-25 15:33:18 +0000
committerMartin Odersky <odersky@gmail.com>2008-01-25 15:33:18 +0000
commit2fd65e0fd34d13c355deeea982f30dce714b6528 (patch)
tree03aa3b83d890338fe982d62fa01be802de9bb0d8
parentbb99cc66c9006735ebf29d8f30f253408f5bc027 (diff)
downloadscala-2fd65e0fd34d13c355deeea982f30dce714b6528.tar.gz
scala-2fd65e0fd34d13c355deeea982f30dce714b6528.tar.bz2
scala-2fd65e0fd34d13c355deeea982f30dce714b6528.zip
case classes can now inherit from case classes.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala15
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala14
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala7
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Unapplies.scala10
-rw-r--r--src/library/scala/util/parsing/combinator1/Parsers.scala22
-rw-r--r--src/library/scala/util/parsing/combinator1/syntactical/TokenParsers.scala21
-rw-r--r--test/files/run/caseclasses.scala11
7 files changed, 51 insertions, 49 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 485690a801..ae44f1b569 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -301,19 +301,12 @@ abstract class RefChecks extends InfoTransform {
* are subtypes of earlier type instances of the same mixin.
* </li>
* <li> <!-- 2 -->
- * Check that case classes do not inherit from case classes.
- * </li>
- * <li> <!-- 3 -->
- * Check that at most one base type is a case-class.
- * </li>
- * <li> <!-- 4 -->
* Check that inner classes do not inherit from Annotation
* </li>
* </ol>
*/
private def validateBaseTypes(clazz: Symbol) {
val seenTypes = new Array[Type](clazz.info.closure.length)
- var seenCaseClass = if (clazz hasFlag CASE) clazz else NoSymbol
/** validate all base types of a class in reverse linear order. */
def validateType(tp: Type) {
@@ -329,14 +322,6 @@ abstract class RefChecks extends InfoTransform {
}
} else {
seenTypes(index) = tp
- // check that case classes do not inherit from case classes
- if (baseClass hasFlag CASE) {
- if (seenCaseClass != NoSymbol && seenCaseClass != baseClass)
- unit.error(clazz.pos, "implementation restriction: case " +
- seenCaseClass + " and case " + baseClass +
- " cannot be combined in one object");
- seenCaseClass = baseClass
- }
// check that inner classes do not inherit from Annotation
if (baseClass == ClassfileAnnotationClass)
if (!clazz.owner.isPackageClass)
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index d9ee9071cd..e3a4ae897d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -254,17 +254,19 @@ trait SyntheticMethods { self: Analyzer =>
if (!phase.erasedTypes) {
try {
if (clazz hasFlag CASE) {
+ val isTop = !(clazz.info.baseClasses.tail exists (_ hasFlag CASE))
// case classes are implicitly declared serializable
clazz.attributes = AnnotationInfo(SerializableAttr.tpe, List(), List()) :: clazz.attributes
- for (stat <- templ.body) {
- if (stat.isDef && stat.symbol.isMethod && stat.symbol.hasFlag(CASEACCESSOR) && !isPublic(stat.symbol)) {
- ts += newAccessorMethod(stat)
- stat.symbol.resetFlag(CASEACCESSOR)
+ if (isTop) {
+ for (stat <- templ.body) {
+ if (stat.isDef && stat.symbol.isMethod && stat.symbol.hasFlag(CASEACCESSOR) && !isPublic(stat.symbol)) {
+ ts += newAccessorMethod(stat)
+ stat.symbol.resetFlag(CASEACCESSOR)
+ }
}
+ if (!inIDE && clazz.info.nonPrivateDecl(nme.tag) == NoSymbol) ts += tagMethod
}
-
- if (!inIDE && clazz.info.nonPrivateDecl(nme.tag) == NoSymbol) ts += tagMethod
if (clazz.isModuleClass) {
if (!hasOverridingImplementation(Object_toString)) ts += moduleToStringMethod
} else {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 6d57902b4b..240bc297cb 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -691,7 +691,9 @@ trait Typers { self: Analyzer =>
// @M: don't check tree.tpe.symbol.typeParams. check tree.tpe.typeParams!!!
// (e.g., m[Int] --> tree.tpe.symbol.typeParams.length == 1, tree.tpe.typeParams.length == 0!)
tree.tpe.typeParams.length != pt.typeParams.length &&
- !(tree.tpe.typeSymbol==AnyClass || tree.tpe.typeSymbol==AllClass || pt == WildcardType )) {
+ !(tree.tpe.typeSymbol==AnyClass ||
+ tree.tpe.typeSymbol==AllClass ||
+ pt == WildcardType )) {
// Check that the actual kind arity (tree.symbol.typeParams.length) conforms to the expected
// kind-arity (pt.typeParams.length). Full checks are done in checkKindBounds in Infer.
// Note that we treat Any and Nothing as kind-polymorphic.
@@ -711,7 +713,8 @@ trait Typers { self: Analyzer =>
val unapply = unapplyMember(extractor.tpe)
val clazz = if (unapply.tpe.paramTypes.length == 1) unapply.tpe.paramTypes.head.typeSymbol
else NoSymbol
- if ((unapply hasFlag CASE) && (clazz hasFlag CASE)) {
+ if ((unapply hasFlag CASE) && (clazz hasFlag CASE) &&
+ !(clazz.info.baseClasses.tail exists (_ hasFlag CASE))) {
if (!phase.erasedTypes) checkStable(tree) //todo: do we need to demand this?
// convert synthetic unapply of case class to case class constructor
val prefix = tree.tpe.prefix
diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
index c0be7a9454..95bc0bce61 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
@@ -121,8 +121,14 @@ trait Unapplies { self: Analyzer =>
def caseFieldAccessorValue(selector: Symbol) = Select(Ident(param), selector)
val accessors = caseclazz.caseFieldAccessors
if (accessors.isEmpty) Literal(true)
- else if (accessors.tail.isEmpty) caseFieldAccessorValue(accessors.head)
- else Apply(gen.scalaDot(newTermName( "Tuple" + accessors.length)), accessors map caseFieldAccessorValue)
+ else
+ Apply(
+ gen.scalaDot(nme.Some),
+ List(
+ if (accessors.tail.isEmpty) caseFieldAccessorValue(accessors.head)
+ else Apply(
+ gen.scalaDot(newTermName("Tuple" + accessors.length)),
+ accessors map caseFieldAccessorValue)))
}
/** The module corresponding to a case class; without any member definitions
diff --git a/src/library/scala/util/parsing/combinator1/Parsers.scala b/src/library/scala/util/parsing/combinator1/Parsers.scala
index cef539cee6..01b16b1917 100644
--- a/src/library/scala/util/parsing/combinator1/Parsers.scala
+++ b/src/library/scala/util/parsing/combinator1/Parsers.scala
@@ -660,6 +660,28 @@ trait Parsers {
}
}
+ /** <p>
+ * A parser generator delimiting whole phrases (i.e. programs).
+ * </p>
+ * <p>
+ * <code>phrase(p)</code> succeeds if <code>p</code> succeeds and
+ * no input is left over after <code>p</code>.
+ * </p>
+ *
+ * @param p the parser that must consume all input for the resulting parser
+ * to succeed.
+ * @return a parser that has the same result as `p', but that only succeeds
+ * if <code>p</code> consumed all the input.
+ */
+ def phrase[T](p: Parser[T]) = new Parser[T] {
+ lastNoSuccess = null
+ def apply(in: Input) = p(in) match {
+ case s @ Success(out, in1) if in1.atEnd => s
+ case s @ Success(out, in1) => Failure("end of input expected", in1)
+ case f : NoSuccess => lastNoSuccess
+ }
+ }
+
case class ~[+a, +b](_1: a, _2: b) {
override def toString = "("+ _1 +" ~ "+ _2 +")"
}
diff --git a/src/library/scala/util/parsing/combinator1/syntactical/TokenParsers.scala b/src/library/scala/util/parsing/combinator1/syntactical/TokenParsers.scala
index 5cb66c5941..6668d8a37e 100644
--- a/src/library/scala/util/parsing/combinator1/syntactical/TokenParsers.scala
+++ b/src/library/scala/util/parsing/combinator1/syntactical/TokenParsers.scala
@@ -34,27 +34,6 @@ trait TokenParsers extends Parsers {
/** The input-type for these parsers*/
type Elem = lexical.Token
- /** <p>
- * A parser generator delimiting whole phrases (i.e. programs).
- * </p>
- * <p>
- * <code>phrase(p)</code> succeeds if <code>p</code> succeeds and
- * no input is left over after <code>p</code>.
- * </p>
- *
- * @param p the parser that must consume all input for the resulting parser
- * to succeed.
- * @return a parser that has the same result as `p', but that only succeeds
- * if <code>p</code> consumed all the input.
- */
- def phrase[t](p: Parser[t]) = new Parser[t] {
- lastNoSuccess = null
- def apply(in: Input) = p(in) match {
- case s @ Success(out, in1) if in1.atEnd => s
- case s @ Success(out, in1) => Failure("end of input expected", in1)
- case f : NoSuccess => lastNoSuccess
- }
- }
}
diff --git a/test/files/run/caseclasses.scala b/test/files/run/caseclasses.scala
index 8cba990114..b971cf3582 100644
--- a/test/files/run/caseclasses.scala
+++ b/test/files/run/caseclasses.scala
@@ -1,6 +1,8 @@
-case class Foo(x: int)(y: int);
+case class Foo(x: int)(y: int)
-case class Bar;
+case class Bar
+
+case class Baz(override val x: Int, y: Int) extends Foo(x)(y)
object M {
abstract case class C(x: String) {}
@@ -20,7 +22,10 @@ object Test extends Application {
case Foo(1) => Console.println("OK")
case Bar() => Console.println("NO")
}
-
+ (Baz(1, 2): AnyRef) match {
+ case Baz(1, 2) => ;
+ case Bar() => Console.println("NO")
+ }
try {
Bar() productElement 3
throw new NullPointerException("duh")