From 644053acbab79776f4a9d4a02b3db5d47fad075c Mon Sep 17 00:00:00 2001
From: "Paolo G. Giarrusso"
Date: Tue, 15 Jan 2019 18:36:11 +0100
Subject: [PATCH 1/2] Refactorings
---
.../dotc/reporting/diagnostic/ErrorMessageID.java | 2 +-
.../tools/dotc/reporting/diagnostic/messages.scala | 2 +-
compiler/src/dotty/tools/dotc/typer/Checking.scala | 10 ++++++----
compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +-
.../tools/dotc/reporting/ErrorMessagesTests.scala | 2 +-
5 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java
index c9d60bdb772b..cc67c76c33b9 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java
@@ -132,7 +132,7 @@ public enum ErrorMessageID {
ImportRenamedTwiceID,
TypeTestAlwaysSucceedsID,
TermMemberNeedsNeedsResultTypeForImplicitSearchID,
- CaseClassCannotExtendEnumID,
+ ClassCannotExtendEnumID,
ValueClassParameterMayNotBeCallByNameID,
NotAnExtractorID,
MemberWithSameNameAsStaticID,
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
index 47c4a34ad972..1197b295cc1c 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -2126,7 +2126,7 @@ object messages {
|""".stripMargin
}
- case class CaseClassCannotExtendEnum(cls: Symbol, parent: Symbol)(implicit ctx: Context) extends Message(CaseClassCannotExtendEnumID) {
+ case class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(implicit ctx: Context) extends Message(ClassCannotExtendEnumID) {
override def kind: String = "Syntax"
override def msg: String = hl"""Normal case class cannot extend an enum. case $cls in ${cls.owner} is extending enum ${parent.name}."""
override def explanation: String = ""
diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala
index a195b81c86b6..8022a3f77e43 100644
--- a/compiler/src/dotty/tools/dotc/typer/Checking.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala
@@ -901,14 +901,16 @@ trait Checking {
}
/** Check that all case classes that extend `scala.Enum` are `enum` cases */
- def checkEnum(cdef: untpd.TypeDef, cls: Symbol, parent: Symbol)(implicit ctx: Context): Unit = {
+ def checkEnum(cdef: untpd.TypeDef, cls: Symbol, firstParent: Symbol)(implicit ctx: Context): Unit = {
import untpd.modsDeco
def isEnumAnonCls =
cls.isAnonymousClass &&
cls.owner.isTerm &&
(cls.owner.flagsUNSAFE.is(Case) || cls.owner.name == nme.DOLLAR_NEW)
- if (!cdef.mods.isEnumCase && !isEnumAnonCls)
- ctx.error(CaseClassCannotExtendEnum(cls, parent), cdef.sourcePos)
+ if (!cdef.mods.isEnumCase && !isEnumAnonCls) {
+ if (cls.is(Case))
+ ctx.error(ClassCannotExtendEnum(cls, firstParent), cdef.sourcePos)
+ }
}
/** Check that all references coming from enum cases in an enum companion object
@@ -975,7 +977,7 @@ trait Checking {
trait ReChecking extends Checking {
import tpd._
- override def checkEnum(cdef: untpd.TypeDef, cls: Symbol, parent: Symbol)(implicit ctx: Context): Unit = ()
+ override def checkEnum(cdef: untpd.TypeDef, cls: Symbol, firstParent: Symbol)(implicit ctx: Context): Unit = ()
override def checkRefsLegal(tree: tpd.Tree, badOwner: Symbol, allowed: (Name, Symbol) => Boolean, where: String)(implicit ctx: Context): Unit = ()
override def checkEnumCaseRefsLegal(cdef: TypeDef, enumCtx: Context)(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 03d33f330927..7705b5c91d86 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1651,7 +1651,7 @@ class Typer extends Namer
.withType(dummy.termRef)
if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper)
checkRealizableBounds(cls, cdef.sourcePos.withSpan(cdef.nameSpan))
- if (cls.is(Case) && cls.derivesFrom(defn.EnumClass)) {
+ if (cls.derivesFrom(defn.EnumClass)) {
val firstParent = parents1.head.tpe.dealias.typeSymbol
checkEnum(cdef, cls, firstParent)
}
diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
index 394afa7a90d9..fdec58fa1933 100644
--- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
+++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
@@ -30,7 +30,7 @@ class ErrorMessagesTests extends ErrorMessagesTest {
implicit val ctx: Context = ictx
assertMessageCount(1, messages)
val errorMsg = messages.head
- val CaseClassCannotExtendEnum(cls, parent) :: Nil = messages
+ val ClassCannotExtendEnum(cls, parent) :: Nil = messages
assertEquals("Bar", cls.name.show)
assertEquals("Foo", parent.name.show)
assertEquals("", cls.owner.name.show)
From 2dfd81afaff4c7127112535ec9f3ccbb7560b3f9 Mon Sep 17 00:00:00 2001
From: "Paolo G. Giarrusso"
Date: Tue, 15 Jan 2019 18:49:40 +0100
Subject: [PATCH 2/2] Fix #5008: forbid any class from extending Enums
---
.../dotty/tools/dotc/reporting/diagnostic/messages.scala | 2 +-
compiler/src/dotty/tools/dotc/typer/Checking.scala | 7 ++++++-
tests/neg/i5008.scala | 8 ++++++++
tests/pending/neg/i5008 | 2 --
4 files changed, 15 insertions(+), 4 deletions(-)
create mode 100644 tests/neg/i5008.scala
delete mode 100644 tests/pending/neg/i5008
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
index 1197b295cc1c..6cbc0526b9d6 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -2128,7 +2128,7 @@ object messages {
case class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(implicit ctx: Context) extends Message(ClassCannotExtendEnumID) {
override def kind: String = "Syntax"
- override def msg: String = hl"""Normal case class cannot extend an enum. case $cls in ${cls.owner} is extending enum ${parent.name}."""
+ override def msg: String = hl"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited."""
override def explanation: String = ""
}
diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala
index 8022a3f77e43..b787dc678367 100644
--- a/compiler/src/dotty/tools/dotc/typer/Checking.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala
@@ -908,7 +908,12 @@ trait Checking {
cls.owner.isTerm &&
(cls.owner.flagsUNSAFE.is(Case) || cls.owner.name == nme.DOLLAR_NEW)
if (!cdef.mods.isEnumCase && !isEnumAnonCls) {
- if (cls.is(Case))
+ // Since enums are classes and Namer checks that classes don't extend multiple classes, we only check the class
+ // parent.
+ //
+ // Unlike firstParent.derivesFrom(defn.EnumClass), this test allows inheriting from `Enum` by hand;
+ // see enum-List-control.scala.
+ if (cls.is(Case) || firstParent.is(Enum))
ctx.error(ClassCannotExtendEnum(cls, firstParent), cdef.sourcePos)
}
}
diff --git a/tests/neg/i5008.scala b/tests/neg/i5008.scala
new file mode 100644
index 000000000000..25e19f423982
--- /dev/null
+++ b/tests/neg/i5008.scala
@@ -0,0 +1,8 @@
+enum Foo { case A }
+enum Bar { case A }
+enum Baz extends Foo { case Z } // error
+
+enum Quux extends Foo with Bar { case Z } // error
+
+class Quuw extends Foo // error
+class Quuz extends Foo { val enumTag = 1 } // error
diff --git a/tests/pending/neg/i5008 b/tests/pending/neg/i5008
deleted file mode 100644
index 2dc75e6b199c..000000000000
--- a/tests/pending/neg/i5008
+++ /dev/null
@@ -1,2 +0,0 @@
-enum Foo {}
-enum Bar extends Foo {} // error