Skip to content

Commit 7b583e6

Browse files
authored
Merge pull request #5031 from dotty-staging/fix-5008-any-extends-enum
Fix #5008: forbid any class extending enums
2 parents dbc7801 + 2dfd81a commit 7b583e6

File tree

7 files changed

+24
-11
lines changed

7 files changed

+24
-11
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public enum ErrorMessageID {
132132
ImportRenamedTwiceID,
133133
TypeTestAlwaysSucceedsID,
134134
TermMemberNeedsNeedsResultTypeForImplicitSearchID,
135-
CaseClassCannotExtendEnumID,
135+
ClassCannotExtendEnumID,
136136
ValueClassParameterMayNotBeCallByNameID,
137137
NotAnExtractorID,
138138
MemberWithSameNameAsStaticID,

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,9 +2126,9 @@ object messages {
21262126
|""".stripMargin
21272127
}
21282128

2129-
case class CaseClassCannotExtendEnum(cls: Symbol, parent: Symbol)(implicit ctx: Context) extends Message(CaseClassCannotExtendEnumID) {
2129+
case class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(implicit ctx: Context) extends Message(ClassCannotExtendEnumID) {
21302130
override def kind: String = "Syntax"
2131-
override def msg: String = hl"""Normal case class cannot extend an enum. case $cls in ${cls.owner} is extending enum ${parent.name}."""
2131+
override def msg: String = hl"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited."""
21322132
override def explanation: String = ""
21332133
}
21342134

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -901,14 +901,21 @@ trait Checking {
901901
}
902902

903903
/** Check that all case classes that extend `scala.Enum` are `enum` cases */
904-
def checkEnum(cdef: untpd.TypeDef, cls: Symbol, parent: Symbol)(implicit ctx: Context): Unit = {
904+
def checkEnum(cdef: untpd.TypeDef, cls: Symbol, firstParent: Symbol)(implicit ctx: Context): Unit = {
905905
import untpd.modsDeco
906906
def isEnumAnonCls =
907907
cls.isAnonymousClass &&
908908
cls.owner.isTerm &&
909909
(cls.owner.flagsUNSAFE.is(Case) || cls.owner.name == nme.DOLLAR_NEW)
910-
if (!cdef.mods.isEnumCase && !isEnumAnonCls)
911-
ctx.error(CaseClassCannotExtendEnum(cls, parent), cdef.sourcePos)
910+
if (!cdef.mods.isEnumCase && !isEnumAnonCls) {
911+
// Since enums are classes and Namer checks that classes don't extend multiple classes, we only check the class
912+
// parent.
913+
//
914+
// Unlike firstParent.derivesFrom(defn.EnumClass), this test allows inheriting from `Enum` by hand;
915+
// see enum-List-control.scala.
916+
if (cls.is(Case) || firstParent.is(Enum))
917+
ctx.error(ClassCannotExtendEnum(cls, firstParent), cdef.sourcePos)
918+
}
912919
}
913920

914921
/** Check that all references coming from enum cases in an enum companion object
@@ -975,7 +982,7 @@ trait Checking {
975982

976983
trait ReChecking extends Checking {
977984
import tpd._
978-
override def checkEnum(cdef: untpd.TypeDef, cls: Symbol, parent: Symbol)(implicit ctx: Context): Unit = ()
985+
override def checkEnum(cdef: untpd.TypeDef, cls: Symbol, firstParent: Symbol)(implicit ctx: Context): Unit = ()
979986
override def checkRefsLegal(tree: tpd.Tree, badOwner: Symbol, allowed: (Name, Symbol) => Boolean, where: String)(implicit ctx: Context): Unit = ()
980987
override def checkEnumCaseRefsLegal(cdef: TypeDef, enumCtx: Context)(implicit ctx: Context): Unit = ()
981988
}

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1655,7 +1655,7 @@ class Typer extends Namer
16551655
.withType(dummy.termRef)
16561656
if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper)
16571657
checkRealizableBounds(cls, cdef.sourcePos.withSpan(cdef.nameSpan))
1658-
if (cls.is(Case) && cls.derivesFrom(defn.EnumClass)) {
1658+
if (cls.derivesFrom(defn.EnumClass)) {
16591659
val firstParent = parents1.head.tpe.dealias.typeSymbol
16601660
checkEnum(cdef, cls, firstParent)
16611661
}

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class ErrorMessagesTests extends ErrorMessagesTest {
3030
implicit val ctx: Context = ictx
3131
assertMessageCount(1, messages)
3232
val errorMsg = messages.head
33-
val CaseClassCannotExtendEnum(cls, parent) :: Nil = messages
33+
val ClassCannotExtendEnum(cls, parent) :: Nil = messages
3434
assertEquals("Bar", cls.name.show)
3535
assertEquals("Foo", parent.name.show)
3636
assertEquals("<empty>", cls.owner.name.show)

tests/neg/i5008.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
enum Foo { case A }
2+
enum Bar { case A }
3+
enum Baz extends Foo { case Z } // error
4+
5+
enum Quux extends Foo with Bar { case Z } // error
6+
7+
class Quuw extends Foo // error
8+
class Quuz extends Foo { val enumTag = 1 } // error

tests/pending/neg/i5008

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)