Skip to content

Commit a037261

Browse files
committed
Fix #5495: Disallow lazy enums
Use the same scheme already used in Parser for avoiding type/term flags mixups also when desugaring enums.
1 parent d45fea0 commit a037261

File tree

6 files changed

+33
-24
lines changed

6 files changed

+33
-24
lines changed

compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ object DesugarEnums {
7070

7171
/** Add implied flags to an enum class or an enum case */
7272
def addEnumFlags(cdef: TypeDef)(implicit ctx: Context): TypeDef =
73-
if (cdef.mods.isEnumClass) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Abstract | Sealed))
74-
else if (isEnumCase(cdef)) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Final))
73+
if (cdef.mods.isEnumClass) cdef.withMods(cdef.mods.withAddedFlags(Abstract | Sealed, cdef.span))
74+
else if (isEnumCase(cdef)) cdef.withMods(cdef.mods.withAddedFlags(Final, cdef.span))
7575
else cdef
7676

7777
private def valuesDot(name: PreName)(implicit src: SourceFile) =

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import core._
66
import Types._, Contexts._, Constants._, Names._, Flags._
77
import Symbols._, StdNames._, Trees._
88
import util.{Property, SourceFile, NoSource}
9+
import util.Spans.Span
910
import language.higherKinds
1011
import annotation.constructorOnly
1112
import annotation.internal.sharable
13+
import Decorators._
1214

1315
object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
1416

@@ -233,6 +235,26 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
233235
if (mods.exists(_ eq mod)) this
234236
else withMods(mods :+ mod)
235237

238+
private def compatible(flags1: FlagSet, flags2: FlagSet): Boolean =
239+
flags1.isEmpty || flags2.isEmpty
240+
|| flags1.isTermFlags && flags2.isTermFlags
241+
|| flags1.isTypeFlags && flags2.isTypeFlags
242+
243+
/** Add `flags` to thos modifier set, checking that there are no type/term conflicts.
244+
* If there are conflicts, issue an error and return the modifiers consisting of
245+
* the added flags only. The reason to do it this way is that the added flags usually
246+
* describe the core of a construct whereas the existing set are the modifiers
247+
* given in the source.
248+
*/
249+
def withAddedFlags(flags: FlagSet, span: Span)(given ctx: Context): Modifiers =
250+
if this.flags.isAllOf(flags) then this
251+
else if compatible(this.flags, flags) then this | flags
252+
else
253+
println(i"BAD")
254+
val what = if flags.isTermFlags then "values" else "types"
255+
ctx.error(em"${(flags & ModifierFlags).flagsString} $what cannot be ${this.flags.flagsString}", ctx.source.atSpan(span))
256+
Modifiers(flags)
257+
236258
/** Modifiers with given list of Mods. It is checked that
237259
* all modifiers are already accounted for in `flags` and `privateWithin`.
238260
*/

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ object Flags {
339339
val (_, DefaultMethod @ _, _) = newFlags(38, "<defaultmethod>")
340340

341341
/** Symbol is an enum class or enum case (if used with case) */
342-
val (Enum @ _, _, _) = newFlags(40, "<enum>")
342+
val (Enum @ _, _, _) = newFlags(40, "enum")
343343

344344
/** An export forwarder */
345345
val (Exported @ _, _, _) = newFlags(41, "exported")

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2631,23 +2631,8 @@ object Parsers {
26312631
addMod(mods, mod)
26322632
}
26332633

2634-
private def compatible(flags1: FlagSet, flags2: FlagSet): Boolean = (
2635-
flags1.isEmpty
2636-
|| flags2.isEmpty
2637-
|| flags1.isTermFlags && flags2.isTermFlags
2638-
|| flags1.isTypeFlags && flags2.isTypeFlags
2639-
)
2640-
2641-
def addFlag(mods: Modifiers, flag: FlagSet): Modifiers = {
2642-
def getPrintableTypeFromFlagSet =
2643-
Map(Trait -> "trait", Method -> "method", Mutable -> "variable").get(flag)
2644-
2645-
if (compatible(mods.flags, flag)) mods | flag
2646-
else {
2647-
syntaxError(ModifiersNotAllowed(mods.flags, getPrintableTypeFromFlagSet))
2648-
Modifiers(flag)
2649-
}
2650-
}
2634+
def addFlag(mods: Modifiers, flag: FlagSet): Modifiers =
2635+
mods.withAddedFlags(flag, Span(in.offset))
26512636

26522637
/** Always add the syntactic `mod`, but check and conditionally add semantic `mod.flags`
26532638
*/

tests/neg/i5495.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
lazy enum LazyList[+A] { // error
2+
case :: (head: A, tail: LazyList[A])
3+
case Nil
4+
}

tests/neg/i6795-b.check

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
-- [E083] Syntax Error: tests/neg/i6795-b.scala:1:11 -------------------------------------------------------------------
1+
-- Error: tests/neg/i6795-b.scala:1:11 ---------------------------------------------------------------------------------
22
1 |sealed def y: Int = 1 // error
33
| ^
4-
| Modifier(s) `sealed` not allowed for method
5-
6-
longer explanation available when compiling with `-explain`
4+
| values cannot be sealed

0 commit comments

Comments
 (0)