diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index e1c1ba075de6..c89fab405b5a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -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 @@ -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 @@ -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. diff --git a/tests/neg-deep-subtype/i9325.scala b/tests/neg-deep-subtype/i9325.scala index e424ea88f350..d850397a83d3 100644 --- a/tests/neg-deep-subtype/i9325.scala +++ b/tests/neg-deep-subtype/i9325.scala @@ -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 diff --git a/tests/neg/enum-constrs.scala b/tests/neg/enum-constrs.scala index 13c39628044d..3a632f412dfa 100644 --- a/tests/neg/enum-constrs.scala +++ b/tests/neg/enum-constrs.scala @@ -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 } diff --git a/tests/neg/i11253.check b/tests/neg/i11253.check new file mode 100644 index 000000000000..38ae15b15af3 --- /dev/null +++ b/tests/neg/i11253.check @@ -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 |} diff --git a/tests/neg/i11253.scala b/tests/neg/i11253.scala new file mode 100644 index 000000000000..73a4559432fd --- /dev/null +++ b/tests/neg/i11253.scala @@ -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] +} + diff --git a/tests/neg/trait-java-enum.scala b/tests/neg/trait-java-enum.scala new file mode 100644 index 000000000000..0ea50e3f187a --- /dev/null +++ b/tests/neg/trait-java-enum.scala @@ -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 +} \ No newline at end of file diff --git a/tests/pos/i11253.scala b/tests/pos/i11253.scala new file mode 100644 index 000000000000..c0c273b9fb24 --- /dev/null +++ b/tests/pos/i11253.scala @@ -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 } diff --git a/tests/pos/trait-java-enum.scala b/tests/pos/trait-java-enum.scala deleted file mode 100644 index 96a2fde59094..000000000000 --- a/tests/pos/trait-java-enum.scala +++ /dev/null @@ -1,5 +0,0 @@ -trait T extends java.lang.Enum[T] - -enum MyEnum extends T { - case A, B -} diff --git a/tests/run/enum-constrs.check b/tests/run/enum-constrs.check index 668ec8702ae8..cf2527a6f025 100644 --- a/tests/run/enum-constrs.check +++ b/tests/run/enum-constrs.check @@ -1,3 +1,2 @@ Red -S1 Car diff --git a/tests/run/enum-constrs.scala b/tests/run/enum-constrs.scala index 1418f39b99b2..7e32659544ee 100644 --- a/tests/run/enum-constrs.scala +++ b/tests/run/enum-constrs.scala @@ -2,11 +2,6 @@ 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) @@ -14,6 +9,5 @@ enum Vehicle(wheels: Int) extends java.lang.Enum[Vehicle] { object Test extends App { println(Color.Red) - println(E.S1) println(Vehicle.Car) } \ No newline at end of file diff --git a/tests/run/enum-custom-toString.scala b/tests/run/enum-custom-toString.scala index 7432bde87ff9..981cdbf481d8 100644 --- a/tests/run/enum-custom-toString.scala +++ b/tests/run/enum-custom-toString.scala @@ -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" @@ -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}") diff --git a/tests/run/enum-values-order.scala b/tests/run/enum-values-order.scala index dfe9c0d8f685..6beeb7ab48dc 100644 --- a/tests/run/enum-values-order.scala +++ b/tests/run/enum-values-order.scala @@ -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]: @@ -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