diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 33121f7be119..797a5e6f3e5f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -638,7 +638,7 @@ trait Checking { * * The standard library relies on this idiom. */ - def checkTraitInheritance(parent: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context): Unit = { + def checkTraitInheritance(parent: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context): Unit = parent match { case parent: ClassSymbol if parent is Trait => val psuper = parent.superClass @@ -650,7 +650,18 @@ trait Checking { ctx.error(em"illegal trait inheritance: super$csuper does not derive from $parent's super$psuper", pos) case _ => } - } + + /** Check that case classes are not inherited by case classes. + */ + def checkCaseInheritance(parent: Symbol, caseCls: ClassSymbol, pos: Position)(implicit ctx: Context): Unit = + parent match { + case parent: ClassSymbol => + if (parent is Case) + ctx.error(ex"""case $caseCls has case ancestor $parent, but case-to-case inheritance is prohibited. + |To overcome this limitation, use extractors to pattern match on non-leaf nodes.""", pos) + else checkCaseInheritance(parent.superClass, caseCls, pos) + case _ => + } /** Check that method parameter types do not reference their own parameter * or later parameters in the same parameter section. @@ -686,5 +697,6 @@ trait NoChecking extends Checking { override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = () override def checkTraitInheritance(parentSym: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context) = () + override def checkCaseInheritance(parentSym: Symbol, caseCls: ClassSymbol, pos: Position)(implicit ctx: Context) = () override def checkNoForwardDependencies(vparams: List[ValDef])(implicit ctx: Context): Unit = () } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 57e6b89b6f19..7dcb9277e4ca 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1343,22 +1343,18 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit ref } - def typedParent(tree: untpd.Tree): Tree = + def typedParent(tree: untpd.Tree): Tree = { + var result = if (tree.isType) typedType(tree)(superCtx) else typedExpr(tree)(superCtx) + val psym = result.tpe.typeSymbol if (tree.isType) { - val result = typedType(tree)(superCtx) - val psym = result.tpe.typeSymbol - checkTraitInheritance(psym, cls, tree.pos) if (psym.is(Trait) && !cls.is(Trait) && !cls.superClass.isSubClass(psym)) - maybeCall(result, psym, psym.primaryConstructor.info) - else - result - } - else { - val result = typedExpr(tree)(superCtx) - checkTraitInheritance(result.symbol, cls, tree.pos) - checkParentCall(result, cls) - result + result = maybeCall(result, psym, psym.primaryConstructor.info) } + else checkParentCall(result, cls) + checkTraitInheritance(psym, cls, tree.pos) + if (cls is Case) checkCaseInheritance(psym, cls, tree.pos) + result + } /** Checks if one of the decls is a type with the same name as class type member in selfType */ def classExistsOnSelf(decls: Scope, self: tpd.ValDef): Boolean = { diff --git a/tests/neg/extendsTest.scala b/tests/neg/extendsTest.scala new file mode 100644 index 000000000000..7c2cf1ac7a85 --- /dev/null +++ b/tests/neg/extendsTest.scala @@ -0,0 +1,4 @@ +case class A() +case class B() extends A() // error +class C extends A() +case class D() extends C // error