Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,8 @@ trait Checking {
* 2. Check that parameterised `enum` cases do not extend java.lang.Enum.
* 3. Check that only a static `enum` base class can extend java.lang.Enum.
* 4. Check that user does not implement an `ordinal` method in the body of an enum class.
* 5. Check that an enum extending java.lang.Enum has no type parameters and
* uses itself as the type argument to java.lang.Enum.
*/
def checkEnum(cdef: untpd.TypeDef, cls: Symbol, firstParent: Symbol)(using Context): Unit = {
def existingDef(sym: Symbol, clazz: ClassSymbol)(using Context): Symbol = // adapted from SyntheticMembers
Expand All @@ -1575,6 +1577,18 @@ trait Checking {
report.error(em"the ordinal method of enum $cls can not be defined by the user", decl.srcPos)
else
report.error(em"enum $cls can not inherit the concrete ordinal method of ${decl.owner}", cdef.srcPos)
def checkJavaEnumTypeArg(using Context) =
val javaEnumBase = cls.thisType.baseType(defn.JavaEnumClass)
if javaEnumBase.exists then
javaEnumBase.argInfos match
case typeArg :: Nil =>
if cls.typeParams.nonEmpty then
report.error(em"An enum extending java.lang.Enum cannot have type parameters", cdef.srcPos)
if !(typeArg =:= cls.typeRef) then
report.error(
em"enum $cls extends java.lang.Enum[$typeArg], but the type argument must be the enum class itself",
cdef.srcPos)
case _ =>
def isEnumAnonCls =
cls.isAnonymousClass
&& cls.owner.isTerm
Expand All @@ -1583,10 +1597,12 @@ trait Checking {
val isJavaEnum = cls.derivesFrom(defn.JavaEnumClass)
if isJavaEnum && cdef.mods.isEnumClass && !cls.isStatic then
report.error(em"An enum extending java.lang.Enum must be declared in a static scope", cdef.srcPos)
if isJavaEnum && cdef.mods.isEnumClass then
checkJavaEnumTypeArg
if !isEnumAnonCls then
if cdef.mods.isEnumCase then
if isJavaEnum then
report.error(em"paramerized case is not allowed in an enum that extends java.lang.Enum", cdef.srcPos)
report.error(em"parameterized case is not allowed in an enum that extends java.lang.Enum", cdef.srcPos)
else if cls.is(Case) || firstParent.is(Enum) then
// Since enums are classes and Namer checks that classes don't extend multiple classes, we only check the class
// parent.
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-deep-subtype/i9325.scala
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
enum Foo[T] extends java.lang.Enum[Foo[T]] { case Red extends Foo[Int]; case Blue extends Foo[String] }
enum Foo[T] extends java.lang.Enum[Foo[T]] { case Red extends Foo[Int]; case Blue extends Foo[String] } // error: An enum extending java.lang.Enum cannot have type parameters
val res0 = (Foo.Red: Foo[?]) compareTo Foo.Blue // error: type mismatch Found (Foo.Blue : Foo[String]) Expected ?1.E
9 changes: 7 additions & 2 deletions tests/neg/enum-constrs.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@

enum E[+T] extends java.lang.Enum[E[_]] {
enum E[+T] extends java.lang.Enum[E[_]] { // error: An enum extending java.lang.Enum cannot have type parameters
case S1, S2
case C() extends E[Int] // error: parameterized case is not allowed
case C() extends E[Int]
}

enum E2 extends java.lang.Enum[E2] {
case S1, S2
case C() extends E2 // error: parameterized case is not allowed
}
35 changes: 35 additions & 0 deletions tests/neg/i11253.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
-- Error: tests/neg/i11253.scala:5:5 -----------------------------------------------------------------------------------
5 |enum E1(val t: String) extends Enum[T], T { case X extends E1("xxx") } // error
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|enum class E1 extends java.lang.Enum[T], but the type argument must be the enum class itself
-- Error: tests/neg/i11253.scala:9:5 -----------------------------------------------------------------------------------
9 |enum E2 extends Enum[Nothing] { case X } // error
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|enum class E2 extends java.lang.Enum[Nothing], but the type argument must be the enum class itself
-- Error: tests/neg/i11253.scala:11:5 ----------------------------------------------------------------------------------
11 |enum E3[A](val inner: A) extends Enum[E3[Int]] { // error
|^
|An enum extending java.lang.Enum cannot have type parameters
|
12 | case X extends E3[String]("hello")
13 |}
-- Error: tests/neg/i11253.scala:17:5 ----------------------------------------------------------------------------------
17 |enum E5[+A] extends Enum[E5[?]] { case X extends E5[Nothing] } // error
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|An enum extending java.lang.Enum cannot have type parameters
-- Error: tests/neg/i11253.scala:19:5 ----------------------------------------------------------------------------------
19 |enum E6[A] extends Enum[E6[?]] { case X extends E6[Unit] } // error
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|An enum extending java.lang.Enum cannot have type parameters
-- Error: tests/neg/i11253.scala:27:5 ----------------------------------------------------------------------------------
27 |enum E7 extends UBad { case X } // error
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|enum class E7 extends java.lang.Enum[UBad], but the type argument must be the enum class itself
-- Error: tests/neg/i11253.scala:31:5 ----------------------------------------------------------------------------------
31 |enum E[+T] extends java.lang.Enum[E[_]] { // error
|^
|An enum extending java.lang.Enum cannot have type parameters
|
32 | case S1, S2
33 | case C extends E[Int]
34 |}
35 changes: 35 additions & 0 deletions tests/neg/i11253.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// --- Cases from #11253 ---

trait T extends Enum[T] { def t: String }

enum E1(val t: String) extends Enum[T], T { case X extends E1("xxx") } // error

// --- Cases from #11252 ---

enum E2 extends Enum[Nothing] { case X } // error

enum E3[A](val inner: A) extends Enum[E3[Int]] { // error
case X extends E3[String]("hello")
}

// --- Java enum behavior: generic enums with wildcard type args rejected ---

enum E5[+A] extends Enum[E5[?]] { case X extends E5[Nothing] } // error

enum E6[A] extends Enum[E6[?]] { case X extends E6[Unit] } // error

// --- Cases from #9541 ---

trait U[E <: Enum[E]] extends Enum[E]

trait UBad extends U[UBad]

enum E7 extends UBad { case X } // error

// --- other cases ---

enum E[+T] extends java.lang.Enum[E[_]] { // error
case S1, S2
case C extends E[Int]
}

5 changes: 5 additions & 0 deletions tests/neg/trait-java-enum.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
trait T extends java.lang.Enum[T]

enum MyEnum extends T { // error: enum class MyEnum extends java.lang.Enum[T], but the type argument must be the enum class itself
case A, B
}
10 changes: 10 additions & 0 deletions tests/pos/i11253.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// --- Valid cases related to #11253/#9541 ---

trait U[E <: Enum[E]] extends Enum[E]
trait TValid extends Enum[E9]

enum E4 extends Enum[E4] { case X }

enum E9 extends Enum[E9] with TValid { case X }

enum E10 extends U[E10] { case X }
5 changes: 0 additions & 5 deletions tests/pos/trait-java-enum.scala

This file was deleted.

1 change: 0 additions & 1 deletion tests/run/enum-constrs.check
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
Red
S1
Car
6 changes: 0 additions & 6 deletions tests/run/enum-constrs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,12 @@ enum Color extends java.lang.Enum[Color] {
case Red, Green, Blue
}

enum E[+T] extends java.lang.Enum[E[_]] {
case S1, S2
case C extends E[Int]
}

enum Vehicle(wheels: Int) extends java.lang.Enum[Vehicle] {
case Bike extends Vehicle(2)
case Car extends Vehicle(4)
}

object Test extends App {
println(Color.Red)
println(E.S1)
println(Vehicle.Car)
}
6 changes: 0 additions & 6 deletions tests/run/enum-custom-toString.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ trait Mixin extends reflect.Enum:
enum EM extends Mixin:
case C

enum ET[T] extends java.lang.Enum[ET[_]]:
case D extends ET[Unit]
override def toString: String = "overridden"

enum EZ:
case E(arg: Int)
override def toString: String = "overridden"
Expand Down Expand Up @@ -53,8 +49,6 @@ object Tag:
assert(EM.C.toString == "overridden", s"EM.C.toString = ${EM.C.toString}")
assert(EM.C.productPrefix == "noprefix", s"EM.C.productPrefix = ${EM.C.productPrefix}")
assert(EM.valueOf("C") == EM.C, s"EM.valueOf(C) = ${EM.valueOf("C")}")
assert(ET.D.toString == "overridden", s"ET.D.toString = ${ET.D.toString}")
assert(ET.D.productPrefix == "D", s"ET.D.productPrefix = ${ET.D.productPrefix}")
assert(EZ.E(0).toString == "overridden", s"EZ.E(0).toString = ${EZ.E(0).toString}")
assert(EZ.E(0).productPrefix == "E", s"EZ.E(0).productPrefix = ${EZ.E(0).productPrefix}")
assert(EC.F.toString == "F", s"EC.F.toString = ${EC.F.toString}")
Expand Down
11 changes: 0 additions & 11 deletions tests/run/enum-values-order.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ enum LatinAlphabet { case A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R,

enum LatinAlphabet2 extends java.lang.Enum[LatinAlphabet2] { case A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z }

enum LatinAlphabet3[+T] extends java.lang.Enum[LatinAlphabet3[_]] { case A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z }

object Color:
trait Pretty
enum Color extends java.lang.Enum[Color]:
Expand Down Expand Up @@ -38,17 +36,8 @@ enum Color extends java.lang.Enum[Color]:
assert(ordinals == ordered.map(_.ordinal))
assert(labels == ordered.map(_.name))

def testLatin3() =
import LatinAlphabet3.*
val ordered = Seq(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z)

assert(ordered sameElements LatinAlphabet3.values)
assert(ordinals == ordered.map(_.ordinal))
assert(labels == ordered.map(_.name))

testLatin1()
testLatin2()
testLatin3()

end testLatin

Expand Down
Loading