diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9d53de5a2482..c4a77f17060c 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -4263,18 +4263,21 @@ object Parsers { finalizeDef(ModuleDef(name, templ), mods, start) } - private def checkAccessOnly(mods: Modifiers, where: String): Modifiers = - // We allow `infix to mark the `enum`s type as infix. - // Syntax rules disallow the soft infix modifier on `case`s. - val mods1 = mods & (AccessFlags | Enum | Infix) - if mods1 ne mods then - syntaxError(em"Only access modifiers are allowed on enum $where") - mods1 + private def checkAccessOnly(mods: Modifiers, caseStr: String): Modifiers = + // We allow `infix` and `into` on `enum` definitions. + // Syntax rules disallow these soft infix modifiers on `case`s. + val flags = mods.flags + var flags1 = flags + for mod <- mods.mods do + if !mod.flags.isOneOf(AccessFlags | Enum | Infix | Into) then + syntaxError(em"This modifier is not allowed on an enum$caseStr", mod.span) + flags1 = flags1 &~ mod.flags + if flags1 != flags then mods.withFlags(flags1) else mods /** EnumDef ::= id ClassConstr InheritClauses EnumBody */ def enumDef(start: Offset, mods: Modifiers): TypeDef = atSpan(start, nameStart) { - val mods1 = checkAccessOnly(mods, "definitions") + val mods1 = checkAccessOnly(mods, "") val modulName = ident() val clsName = modulName.toTypeName val constr = classConstr(ParamOwner.Class) @@ -4285,7 +4288,7 @@ object Parsers { /** EnumCase = `case' (id ClassConstr [`extends' ConstrApps] | ids) */ def enumCase(start: Offset, mods: Modifiers): DefTree = { - val mods1 = checkAccessOnly(mods, "cases") | EnumCase + val mods1 = checkAccessOnly(mods, " case") | EnumCase accept(CASE) atSpan(start, nameStart) { diff --git a/docs/_docs/reference/enums/enums.md b/docs/_docs/reference/enums/enums.md index 4cad29cbd76a..3bd40f88c368 100644 --- a/docs/_docs/reference/enums/enums.md +++ b/docs/_docs/reference/enums/enums.md @@ -117,6 +117,9 @@ The fields referenced by `Mercury` are not visible, and the fields referenced by be referenced directly (using `import Planet.*`). You must use an indirect reference, such as demonstrated with `Earth`. +Enum cases accept only access modifiers. +Enum classes accept only access modifiers and `into` or `infix`. + ## Deprecation of Enum Cases As a library author, you may want to signal that an enum case is no longer intended for use. However you could still want to gracefully handle the removal of a case from your public API, such as special casing deprecated cases. diff --git a/tests/neg/i23400.scala b/tests/neg/i23400.scala index 08c75d279b87..99a41d7a072b 100644 --- a/tests/neg/i23400.scala +++ b/tests/neg/i23400.scala @@ -14,7 +14,7 @@ case class Baz(foo: MyInto[Foo]) given Conversion[Int, Foo] = Foo(_) -into enum Color: // error +into enum Color: // ok case Red, Green def test = diff --git a/tests/neg/i5525.check b/tests/neg/i5525.check new file mode 100644 index 000000000000..81e352cd368f --- /dev/null +++ b/tests/neg/i5525.check @@ -0,0 +1,114 @@ +-- Error: tests/neg/i5525.scala:1:0 ------------------------------------------------------------------------------------ +1 |abstract enum Foo1 { case C } // error: only access modifiers allowed + |^^^^^^^^ + |This modifier is not allowed on an enum +-- Error: tests/neg/i5525.scala:2:0 ------------------------------------------------------------------------------------ +2 |final enum Foo2 { case C } // error: only access modifiers allowed + |^^^^^ + |This modifier is not allowed on an enum +-- Error: tests/neg/i5525.scala:3:0 ------------------------------------------------------------------------------------ +3 |sealed enum Foo3 { case C } // error: only access modifiers allowed + |^^^^^^ + |This modifier is not allowed on an enum +-- Error: tests/neg/i5525.scala:4:0 ------------------------------------------------------------------------------------ +4 |implicit enum Foo4 { case C } // error: only access modifiers allowed + |^^^^^^^^ + |This modifier is not allowed on an enum +-- Error: tests/neg/i5525.scala:5:0 ------------------------------------------------------------------------------------ +5 |lazy enum Foo5 { case C } // error: only access modifiers allowed + |^^^^ + |This modifier is not allowed on an enum +-- Error: tests/neg/i5525.scala:6:0 ------------------------------------------------------------------------------------ +6 |override enum Foo7 { case C } // error: only access modifiers allowed + |^^^^^^^^ + |This modifier is not allowed on an enum +-- Error: tests/neg/i5525.scala:7:0 ------------------------------------------------------------------------------------ +7 |inline enum Foo8 { case C } // error: only access modifiers allowed + |^^^^^^ + |This modifier is not allowed on an enum +-- Error: tests/neg/i5525.scala:8:0 ------------------------------------------------------------------------------------ +8 |opaque enum Foo9 { case C } // error: only access modifiers allowed + |^^^^^^ + |This modifier is not allowed on an enum +-- Error: tests/neg/i5525.scala:11:2 ----------------------------------------------------------------------------------- +11 | abstract case C1() // error: only access modifiers allowed + | ^^^^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:12:2 ----------------------------------------------------------------------------------- +12 | final case C2() // error: only access modifiers allowed + | ^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:13:2 ----------------------------------------------------------------------------------- +13 | sealed case C3() // error: only access modifiers allowed + | ^^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:14:2 ----------------------------------------------------------------------------------- +14 | implicit case C4() // error: only access modifiers allowed + | ^^^^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:15:2 ----------------------------------------------------------------------------------- +15 | lazy case C5() // error: only access modifiers allowed + | ^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:16:2 ----------------------------------------------------------------------------------- +16 | override case C7() // error: only access modifiers allowed + | ^^^^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:22:2 ----------------------------------------------------------------------------------- +22 | abstract case C1 // error: only access modifiers allowed + | ^^^^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:23:2 ----------------------------------------------------------------------------------- +23 | final case C2 // error: only access modifiers allowed + | ^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:24:2 ----------------------------------------------------------------------------------- +24 | sealed case C3 // error: only access modifiers allowed + | ^^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:25:2 ----------------------------------------------------------------------------------- +25 | implicit case C4 // error: only access modifiers allowed + | ^^^^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:26:2 ----------------------------------------------------------------------------------- +26 | lazy case C5 // error: only access modifiers allowed + | ^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:27:2 ----------------------------------------------------------------------------------- +27 | override case C7 // error: only access modifiers allowed + | ^^^^^^^^ + | This modifier is not allowed on an enum case +-- Error: tests/neg/i5525.scala:33:12 ---------------------------------------------------------------------------------- +33 | inline case C10() // error: only access modifiers allowed + | ^^^^ + | end of statement expected but 'case' found +-- Error: tests/neg/i5525.scala:36:0 ----------------------------------------------------------------------------------- +36 |final enum Foo13 { // error: only access modifiers and `into` allowed + |^^^^^ + |This modifier is not allowed on an enum +-- Error: tests/neg/i5525.scala:42:8 ----------------------------------------------------------------------------------- +42 | infix case C2 extends Foo14[Int, Int] // error // error + | ^^^^ + | end of statement expected but 'case' found +-- Error: tests/neg/i5525.scala:49:7 ----------------------------------------------------------------------------------- +49 | into case C1 // error + | ^^^^ + | end of statement expected but 'case' found +-- [E145] Syntax Error: tests/neg/i5525.scala:32:5 --------------------------------------------------------------------- +32 |enum Foo12 { // error: Enumerations must contain at least one case + | ^^^^^ + | Enumerations must contain at least one case + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/i5525.scala:42:2 ------------------------------------------------------------------ +42 | infix case C2 extends Foo14[Int, Int] // error // error + | ^^^^^ + | Not found: infix + | + | longer explanation available when compiling with `-explain` +-- [E129] Potential Issue Warning: tests/neg/i5525.scala:49:2 ---------------------------------------------------------- +49 | into case C1 // error + | ^^^^ + | A pure expression does nothing in statement position + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i5525.scala b/tests/neg/i5525.scala index 12ffb4704ba9..eff8d07fdddd 100644 --- a/tests/neg/i5525.scala +++ b/tests/neg/i5525.scala @@ -1,11 +1,11 @@ -abstract enum Foo1 {} // error: only access modifiers allowed -final enum Foo2 {} // error: only access modifiers allowed -sealed enum Foo3 {} // error: only access modifiers allowed -implicit enum Foo4 {} // error: only access modifiers allowed -lazy enum Foo5 {} // error: only access modifiers allowed -override enum Foo7 {} // error: only access modifiers allowed -inline enum Foo8 {} // error: only access modifiers allowed -opaque enum Foo9 {} // error: only access modifiers allowed +abstract enum Foo1 { case C } // error: only access modifiers allowed +final enum Foo2 { case C } // error: only access modifiers allowed +sealed enum Foo3 { case C } // error: only access modifiers allowed +implicit enum Foo4 { case C } // error: only access modifiers allowed +lazy enum Foo5 { case C } // error: only access modifiers allowed +override enum Foo7 { case C } // error: only access modifiers allowed +inline enum Foo8 { case C } // error: only access modifiers allowed +opaque enum Foo9 { case C } // error: only access modifiers allowed enum Foo10 { abstract case C1() // error: only access modifiers allowed @@ -31,4 +31,20 @@ enum Foo11 { enum Foo12 { // error: Enumerations must contain at least one case inline case C10() // error: only access modifiers allowed +} + +final enum Foo13 { // error: only access modifiers and `into` allowed + case C1 +} + +infix enum Foo14[A, B]{ // OK + case C1 extends Foo14[Int, Int] + infix case C2 extends Foo14[Int, Int] // error // error +} + +import language.experimental.into + +into enum Foo15 { // OK + case C0 + into case C1 // error } \ No newline at end of file diff --git a/tests/neg/i5525b.scala b/tests/neg/i5525b.scala index d51564ad52c1..78bad61e3d50 100644 --- a/tests/neg/i5525b.scala +++ b/tests/neg/i5525b.scala @@ -1,6 +1,6 @@ //> using options -language:experimental.erasedDefinitions -erased enum Foo6 {} // error: only access modifiers allowed +erased enum Foo6 { case C } // error: only access modifiers allowed enum Foo10 { // error: Enumerations must contain at least one case erased case C6() // error // error diff --git a/tests/pos/infix-enum.scala b/tests/pos/infix-enum.scala new file mode 100644 index 000000000000..35f46bcc6e14 --- /dev/null +++ b/tests/pos/infix-enum.scala @@ -0,0 +1,5 @@ +infix enum Foo[A, B]: + case C1 extends Foo[Int, Int] + +val x: Int Foo Int = Foo.C1 +