From 2fd65e0fd34d13c355deeea982f30dce714b6528 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 25 Jan 2008 15:33:18 +0000 Subject: case classes can now inherit from case classes. --- .../scala/tools/nsc/typechecker/RefChecks.scala | 15 --------------- .../tools/nsc/typechecker/SyntheticMethods.scala | 14 ++++++++------ .../scala/tools/nsc/typechecker/Typers.scala | 7 +++++-- .../scala/tools/nsc/typechecker/Unapplies.scala | 10 ++++++++-- .../scala/util/parsing/combinator1/Parsers.scala | 22 ++++++++++++++++++++++ .../combinator1/syntactical/TokenParsers.scala | 21 --------------------- test/files/run/caseclasses.scala | 11 ++++++++--- 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. * *
  • - * Check that case classes do not inherit from case classes. - *
  • - *
  • - * Check that at most one base type is a case-class. - *
  • - *
  • * Check that inner classes do not inherit from Annotation *
  • * */ 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 { } } + /**

    + * A parser generator delimiting whole phrases (i.e. programs). + *

    + *

    + * phrase(p) succeeds if p succeeds and + * no input is left over after 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 p 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 - /**

    - * A parser generator delimiting whole phrases (i.e. programs). - *

    - *

    - * phrase(p) succeeds if p succeeds and - * no input is left over after 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 p 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") -- cgit v1.2.3