From 41d83d42650d0c0b54c47c1a9043d0b92315aa4e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 8 Feb 2017 21:19:16 +1100 Subject: Change handling of enum defs. The previous scheme did not work because desugaring cannot deal with repeated expansions. We now sidestep the issue by doing the expansion in the parser. Luckily, positions work out perfectly, so that one can reconstruct the source precisely from the parsed untyped trees. --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 8 +++-- .../src/dotty/tools/dotc/parsing/Parsers.scala | 41 +++++++++++++++------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index e14c6714b..7020e4dac 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -9,6 +9,7 @@ import Decorators._ import util.Property import language.higherKinds import collection.mutable.ListBuffer +import reflect.ClassTag object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { @@ -39,9 +40,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this)(name.toTermName, impl) } - /** mods case name impl */ - case class EnumDef(name: TypeName, impl: Template) extends MemberDef - case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree) extends TermTree case class SymbolLit(str: String) extends TermTree @@ -192,6 +190,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def hasFlags = flags != EmptyFlags def hasAnnotations = annotations.nonEmpty def hasPrivateWithin = privateWithin != tpnme.EMPTY + def hasMod[T: ClassTag] = { + val cls = implicitly[ClassTag[T]].runtimeClass + mods.exists(mod => cls.isAssignableFrom(mod.getClass)) + } } @sharable val EmptyModifiers: Modifiers = new Modifiers() diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7ec44e342..ee736179a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -49,6 +49,13 @@ object Parsers { val Class, Type, TypeParam, Def = Value } + private implicit class AddDeco(val buf: ListBuffer[Tree]) extends AnyVal { + def +++=(x: Tree) = x match { + case x: Thicket => buf ++= x.trees + case x => buf += x + } + } + /** The parse starting point depends on whether the source file is self-contained: * if not, the AST will be supplemented. */ @@ -2061,8 +2068,9 @@ object Parsers { case CASEOBJECT => objectDef(start, posMods(start, mods | Case | Module)) case ENUM => - val mods1 = addMod(mods, atPos(in.skipToken()) { Mod.Enum() }) - if (in.token == CLASS) tmplDef(start, mods1) else enumDef(start, mods) + val enumMod = atPos(in.skipToken()) { Mod.Enum() } + if (in.token == CLASS) tmplDef(start, addMod(mods, enumMod)) + else enumDef(start, mods, enumMod) case _ => syntaxErrorOrIncomplete("expected start of definition") EmptyTree @@ -2115,9 +2123,11 @@ object Parsers { /** id ClassConstr [`extends' [ConstrApps]] * [nl] ‘{’ EnumCaseStats ‘}’ */ - def enumDef(start: Offset, mods: Modifiers): EnumDef = atPos(start, nameStart) { - val name = ident().toTypeName - val constr = classConstr(name) + def enumDef(start: Offset, mods: Modifiers, enumMod: Mod): Thicket = { + val point = nameStart + val modName = ident() + val clsName = modName.toTypeName + val constr = classConstr(clsName) val parents = if (in.token == EXTENDS) { in.nextToken(); @@ -2125,10 +2135,17 @@ object Parsers { if (in.token == LBRACE) Nil else tokenSeparated(WITH, constrApp) } else Nil + val clsDef = atPos(start, point) { + TypeDef(clsName, Template(constr, parents, EmptyValDef, Nil)) + .withMods(addMod(mods, enumMod)).setComment(in.getDocComment(start)) + } newLineOptWhenFollowedBy(LBRACE) - val body = inBraces(enumCaseStats) - EnumDef(name, Template(constr, Nil, EmptyValDef, body)) - .withMods(mods).setComment(in.getDocComment(start)).asInstanceOf[EnumDef] + val modDef = atPos(in.offset) { + val body = inBraces(enumCaseStats) + ModuleDef(modName, Template(emptyConstructor, Nil, EmptyValDef, body)) + .withMods(mods) + } + Thicket(clsDef :: modDef :: Nil) } /** EnumCaseStats = EnumCaseStat {semi EnumCaseStat */ @@ -2249,7 +2266,7 @@ object Parsers { else if (in.token == IMPORT) stats ++= importClause() else if (in.token == AT || isTemplateIntro || isModifier) - stats += tmplDef(in.offset, defAnnotsMods(modifierTokens)) + stats +++= tmplDef(in.offset, defAnnotsMods(modifierTokens)) else if (!isStatSep) { if (in.token == CASE) syntaxErrorOrIncomplete("only `case class` or `case object` allowed") @@ -2299,7 +2316,7 @@ object Parsers { else if (isExprIntro) stats += expr1() else if (isDefIntro(modifierTokensOrCase)) - stats += defOrDcl(in.offset, defAnnotsMods(modifierTokens)) + stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens)) else if (!isStatSep) { exitOnError = mustStartStat syntaxErrorOrIncomplete("illegal start of definition") @@ -2357,9 +2374,9 @@ object Parsers { val start = in.offset val imods = implicitMods() if (isBindingIntro) stats += implicitClosure(start, Location.InBlock, imods) - else stats += localDef(start, imods) + else stats +++= localDef(start, imods) } else { - stats += localDef(in.offset) + stats +++= localDef(in.offset) } else if (!isStatSep && (in.token != CASE)) { exitOnError = mustStartStat -- cgit v1.2.3