aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorodersky <odersky@gmail.com>2017-04-06 13:12:05 +0200
committerGitHub <noreply@github.com>2017-04-06 13:12:05 +0200
commit62c2a1e2d6265cf7f096e4c4e51e4e883bce1514 (patch)
tree5cd2f9018b4a5ed5fcd1ebe6a6aed392d3fd00b4
parent2556c83a04af1baf9dd69f6139e9ea61d39e7c6a (diff)
parent30d8d878118c537ff82c88ef7ade8780b390bfae (diff)
downloaddotty-62c2a1e2d6265cf7f096e4c4e51e4e883bce1514.tar.gz
dotty-62c2a1e2d6265cf7f096e4c4e51e4e883bce1514.tar.bz2
dotty-62c2a1e2d6265cf7f096e4c4e51e4e883bce1514.zip
Merge pull request #1958 from dotty-staging/add-enum
Add "enum" construct
-rw-r--r--compiler/src/dotty/tools/dotc/ast/Desugar.scala129
-rw-r--r--compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala200
-rw-r--r--compiler/src/dotty/tools/dotc/ast/untpd.scala9
-rw-r--r--compiler/src/dotty/tools/dotc/core/Contexts.scala5
-rw-r--r--compiler/src/dotty/tools/dotc/core/Decorators.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/core/Flags.scala17
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/core/SymDenotations.scala13
-rw-r--r--compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/parsing/Parsers.scala177
-rw-r--r--compiler/src/dotty/tools/dotc/parsing/Scanners.scala67
-rw-r--r--compiler/src/dotty/tools/dotc/parsing/Tokens.scala8
-rw-r--r--compiler/src/dotty/tools/dotc/printing/Formatting.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Applications.scala9
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Namer.scala1
-rw-r--r--compiler/test/dotty/tools/dotc/parsing/DocstringTests.scala30
-rw-r--r--compiler/test/dotty/tools/dotc/repl/TestREPL.scala7
-rw-r--r--docs/docs/internals/syntax.md15
-rw-r--r--library/src/scala/Enum.scala8
-rw-r--r--library/src/scala/runtime/EnumValues.scala21
-rw-r--r--tests/neg/enums.scala20
-rw-r--r--tests/pos/enum-List-control.scala14
-rw-r--r--tests/run/enum-Color.check3
-rw-r--r--tests/run/enum-Color.scala11
-rw-r--r--tests/run/enum-HList.scala22
-rw-r--r--tests/run/enum-List1.check1
-rw-r--r--tests/run/enum-List1.scala10
-rw-r--r--tests/run/enum-List2.check1
-rw-r--r--tests/run/enum-List2.scala11
-rw-r--r--tests/run/enum-List2a.check1
-rw-r--r--tests/run/enum-List2a.scala11
-rw-r--r--tests/run/enum-List3.check1
-rw-r--r--tests/run/enum-List3.scala10
-rw-r--r--tests/run/enum-Option.scala19
-rw-r--r--tests/run/enum-Tree.check1
-rw-r--r--tests/run/enum-Tree.scala29
-rw-r--r--tests/run/enum-approx.scala22
-rw-r--r--tests/run/enumList2a.check1
-rw-r--r--tests/run/generic/Color.scala13
-rw-r--r--tests/run/generic/Enum.scala18
-rw-r--r--tests/run/generic/List.scala2
-rw-r--r--tests/run/generic/SearchResult.scala17
-rw-r--r--tests/run/generic/Tree.scala6
-rw-r--r--tests/run/planets.check8
-rw-r--r--tests/run/planets.scala26
47 files changed, 834 insertions, 184 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala
index 75c7078a1..8499330fb 100644
--- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala
@@ -14,6 +14,7 @@ import reporting.diagnostic.messages._
object desugar {
import untpd._
+ import DesugarEnums._
/** Tags a .withFilter call generated by desugaring a for expression.
* Such calls can alternatively be rewritten to use filter.
@@ -263,7 +264,9 @@ object desugar {
val className = checkNotReservedName(cdef).asTypeName
val impl @ Template(constr0, parents, self, _) = cdef.rhs
val mods = cdef.mods
- val companionMods = mods.withFlags((mods.flags & AccessFlags).toCommonFlags)
+ val companionMods = mods
+ .withFlags((mods.flags & AccessFlags).toCommonFlags)
+ .withMods(mods.mods.filter(!_.isInstanceOf[Mod.EnumCase]))
val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match {
case meth: DefDef => (meth, Nil)
@@ -288,17 +291,31 @@ object desugar {
}
val isCaseClass = mods.is(Case) && !mods.is(Module)
+ val isEnum = mods.hasMod[Mod.Enum]
+ val isEnumCase = isLegalEnumCase(cdef)
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.
- val constrTparams = constr1.tparams map toDefParam
+ lazy val reconstitutedTypeParams = reconstitutedEnumTypeParams(cdef.pos.startPos)
+
+ val originalTparams =
+ if (isEnumCase && parents.isEmpty) {
+ if (constr1.tparams.nonEmpty) {
+ if (reconstitutedTypeParams.nonEmpty)
+ ctx.error(em"case with type parameters needs extends clause", constr1.tparams.head.pos)
+ constr1.tparams
+ }
+ else reconstitutedTypeParams
+ }
+ else constr1.tparams
+ val originalVparamss = constr1.vparamss
+ val constrTparams = originalTparams.map(toDefParam)
val constrVparamss =
- if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty
- if (isCaseClass)
- ctx.error(CaseClassMissingParamList(cdef), cdef.namePos)
+ if (originalVparamss.isEmpty) { // ensure parameter list is non-empty
+ if (isCaseClass) ctx.error(CaseClassMissingParamList(cdef), cdef.namePos)
ListOfNil
}
- else constr1.vparamss.nestedMap(toDefParam)
+ else originalVparamss.nestedMap(toDefParam)
val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss)
// Add constructor type parameters and evidence implicit parameters
@@ -312,21 +329,24 @@ object desugar {
stat
}
- val derivedTparams = constrTparams map derivedTypeParam
+ val derivedTparams =
+ if (isEnumCase) constrTparams else constrTparams map derivedTypeParam
val derivedVparamss = constrVparamss nestedMap derivedTermParam
val arity = constrVparamss.head.length
- var classTycon: Tree = EmptyTree
+ val classTycon: Tree = new TypeRefTree // watching is set at end of method
- // a reference to the class type, with all parameters given.
- val classTypeRef/*: Tree*/ = {
- // -language:keepUnions difference: classTypeRef needs type annotation, otherwise
- // infers Ident | AppliedTypeTree, which
- // renders the :\ in companions below untypable.
- classTycon = (new TypeRefTree) withPos cdef.pos.startPos // watching is set at end of method
- val tparams = impl.constr.tparams
- if (tparams.isEmpty) classTycon else AppliedTypeTree(classTycon, tparams map refOfDef)
- }
+ def appliedRef(tycon: Tree) =
+ (if (constrTparams.isEmpty) tycon
+ else AppliedTypeTree(tycon, constrTparams map refOfDef))
+ .withPos(cdef.pos.startPos)
+
+ // a reference to the class type bound by `cdef`, with type parameters coming from the constructor
+ val classTypeRef = appliedRef(classTycon)
+ // a reference to `enumClass`, with type parameters coming from the constructor
+ lazy val enumClassTypeRef =
+ if (reconstitutedTypeParams.isEmpty) enumClassRef
+ else appliedRef(enumClassRef)
// new C[Ts](paramss)
lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef)
@@ -374,7 +394,9 @@ object desugar {
DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr)
.withMods(synthetic) :: Nil
}
- copyMeths ::: productElemMeths.toList
+
+ val enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind.Class)._1 :: Nil else Nil
+ copyMeths ::: enumTagMeths ::: productElemMeths.toList
}
else Nil
@@ -387,8 +409,12 @@ object desugar {
// Case classes and case objects get a ProductN parent
var parents1 = parents
+ if (isEnumCase && parents.isEmpty)
+ parents1 = enumClassTypeRef :: Nil
if (mods.is(Case) && arity <= Definitions.MaxTupleArity)
- parents1 = parents1 :+ productConstr(arity)
+ parents1 = parents1 :+ productConstr(arity) // TODO: This also adds Product0 to caes objects. Do we want that?
+ if (isEnum)
+ parents1 = parents1 :+ ref(defn.EnumType)
// The thicket which is the desugared version of the companion object
// synthetic object C extends parentTpt { defs }
@@ -410,17 +436,26 @@ object desugar {
// For all other classes, the parent is AnyRef.
val companions =
if (isCaseClass) {
+ // The return type of the `apply` method
+ val applyResultTpt =
+ if (isEnumCase)
+ if (parents.isEmpty) enumClassTypeRef
+ else parents.reduceLeft(AndTypeTree)
+ else TypeTree()
+
val parent =
if (constrTparams.nonEmpty ||
constrVparamss.length > 1 ||
mods.is(Abstract) ||
constr.mods.is(Private)) anyRef
+ else
// todo: also use anyRef if constructor has a dependent method type (or rule that out)!
- else (constrVparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe))
+ (constrVparamss :\ (if (isEnumCase) applyResultTpt else classTypeRef)) (
+ (vparams, restpe) => Function(vparams map (_.tpt), restpe))
val applyMeths =
if (mods is Abstract) Nil
else
- DefDef(nme.apply, derivedTparams, derivedVparamss, TypeTree(), creatorExpr)
+ DefDef(nme.apply, derivedTparams, derivedVparamss, applyResultTpt, creatorExpr)
.withFlags(Synthetic | (constr1.mods.flags & DefaultParameterized)) :: Nil
val unapplyMeth = {
val unapplyParam = makeSyntheticParameter(tpt = classTypeRef)
@@ -464,15 +499,15 @@ object desugar {
else cpy.ValDef(self)(tpt = selfType).withMods(self.mods | SelfName)
}
- val cdef1 = {
- val originalTparams = constr1.tparams.toIterator
- val originalVparams = constr1.vparamss.toIterator.flatten
- val tparamAccessors = derivedTparams.map(_.withMods(originalTparams.next.mods))
+ val cdef1 = addEnumFlags {
+ val originalTparamsIt = originalTparams.toIterator
+ val originalVparamsIt = originalVparamss.toIterator.flatten
+ val tparamAccessors = derivedTparams.map(_.withMods(originalTparamsIt.next.mods))
val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags
val vparamAccessors = derivedVparamss match {
case first :: rest =>
- first.map(_.withMods(originalVparams.next.mods | caseAccessor)) ++
- rest.flatten.map(_.withMods(originalVparams.next.mods))
+ first.map(_.withMods(originalVparamsIt.next.mods | caseAccessor)) ++
+ rest.flatten.map(_.withMods(originalVparamsIt.next.mods))
case _ =>
Nil
}
@@ -503,23 +538,26 @@ object desugar {
*/
def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = {
val moduleName = checkNotReservedName(mdef).asTermName
- val tmpl = mdef.impl
+ val impl = mdef.impl
val mods = mdef.mods
+ lazy val isEnumCase = isLegalEnumCase(mdef)
if (mods is Package)
- PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, tmpl).withMods(mods &~ Package) :: Nil)
+ PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, impl).withMods(mods &~ Package) :: Nil)
+ else if (isEnumCase)
+ expandEnumModule(moduleName, impl, mods, mdef.pos)
else {
val clsName = moduleName.moduleClassName
val clsRef = Ident(clsName)
val modul = ValDef(moduleName, clsRef, New(clsRef, Nil))
.withMods(mods | ModuleCreationFlags | mods.flags & AccessFlags)
.withPos(mdef.pos)
- val ValDef(selfName, selfTpt, _) = tmpl.self
- val selfMods = tmpl.self.mods
- if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), tmpl.self.pos)
- val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(moduleName)), tmpl.self.rhs)
+ val ValDef(selfName, selfTpt, _) = impl.self
+ val selfMods = impl.self.mods
+ if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), impl.self.pos)
+ val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(moduleName)), impl.self.rhs)
.withMods(selfMods)
- .withPos(tmpl.self.pos orElse tmpl.pos.startPos)
- val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body)
+ .withPos(impl.self.pos orElse impl.pos.startPos)
+ val clsTmpl = cpy.Template(impl)(self = clsSelf, body = impl.body)
val cls = TypeDef(clsName, clsTmpl)
.withMods(mods.toTypeFlags & RetainedModuleClassFlags | ModuleClassCreationFlags)
Thicket(modul, classDef(cls).withPos(mdef.pos))
@@ -542,11 +580,23 @@ object desugar {
/** val p1, ..., pN: T = E
* ==>
* makePatDef[[val p1: T1 = E]]; ...; makePatDef[[val pN: TN = E]]
+ *
+ * case e1, ..., eN
+ * ==>
+ * expandSimpleEnumCase([case e1]); ...; expandSimpleEnumCase([case eN])
*/
- def patDef(pdef: PatDef)(implicit ctx: Context): Tree = {
+ def patDef(pdef: PatDef)(implicit ctx: Context): Tree = flatTree {
val PatDef(mods, pats, tpt, rhs) = pdef
- val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
- flatTree(pats1 map (makePatDef(pdef, mods, _, rhs)))
+ if (mods.hasMod[Mod.EnumCase] && enumCaseIsLegal(pdef))
+ pats map {
+ case id: Ident =>
+ expandSimpleEnumCase(id.name.asTermName, mods,
+ Position(pdef.pos.start, id.pos.end, id.pos.start))
+ }
+ else {
+ val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
+ pats1 map (makePatDef(pdef, mods, _, rhs))
+ }
}
/** If `pat` is a variable pattern,
@@ -923,7 +973,7 @@ object desugar {
case (gen: GenFrom) :: (rest @ (GenFrom(_, _) :: _)) =>
val cont = makeFor(mapName, flatMapName, rest, body)
Apply(rhsSelect(gen, flatMapName), makeLambda(gen.pat, cont))
- case (enum @ GenFrom(pat, rhs)) :: (rest @ GenAlias(_, _) :: _) =>
+ case (GenFrom(pat, rhs)) :: (rest @ GenAlias(_, _) :: _) =>
val (valeqs, rest1) = rest.span(_.isInstanceOf[GenAlias])
val pats = valeqs map { case GenAlias(pat, _) => pat }
val rhss = valeqs map { case GenAlias(_, rhs) => rhs }
@@ -1024,7 +1074,6 @@ object desugar {
List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))),
finalizer)
}
-
}
}.withPos(tree.pos)
diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
new file mode 100644
index 000000000..43f915961
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
@@ -0,0 +1,200 @@
+package dotty.tools
+package dotc
+package ast
+
+import core._
+import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
+import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
+import Decorators._
+import collection.mutable.ListBuffer
+import util.Property
+import typer.ErrorReporting._
+
+/** Helper methods to desugar enums */
+object DesugarEnums {
+ import untpd._
+ import desugar.DerivedFromParamTree
+
+ @sharable object CaseKind extends Enumeration {
+ val Simple, Object, Class = Value
+ }
+
+ /** Attachment containing the number of enum cases and the smallest kind that was seen so far. */
+ val EnumCaseCount = new Property.Key[(Int, CaseKind.Value)]
+
+ /** the enumeration class that is a companion of the current object */
+ def enumClass(implicit ctx: Context) = ctx.owner.linkedClass
+
+ /** Is this an enum case that's situated in a companion object of an enum class? */
+ def isLegalEnumCase(tree: MemberDef)(implicit ctx: Context): Boolean =
+ tree.mods.hasMod[Mod.EnumCase] && enumCaseIsLegal(tree)
+
+ /** Is enum case `tree` situated in a companion object of an enum class? */
+ def enumCaseIsLegal(tree: Tree)(implicit ctx: Context): Boolean = (
+ ctx.owner.is(ModuleClass) && enumClass.derivesFrom(defn.EnumClass)
+ || { ctx.error(em"case not allowed here, since owner ${ctx.owner} is not an `enum' object", tree.pos)
+ false
+ }
+ )
+
+ /** Type parameters reconstituted from the constructor
+ * of the `enum' class corresponding to an enum case.
+ * The variance is the same as the corresponding type parameter of the enum class.
+ */
+ def reconstitutedEnumTypeParams(pos: Position)(implicit ctx: Context) = {
+ val tparams = enumClass.primaryConstructor.info match {
+ case info: PolyType =>
+ ctx.newTypeParams(ctx.newLocalDummy(enumClass), info.paramNames, EmptyFlags, info.instantiateBounds)
+ case _ =>
+ Nil
+ }
+ (tparams, enumClass.typeParams).zipped.map { (tparam, ecTparam) =>
+ val tbounds = new DerivedFromParamTree
+ tbounds.pushAttachment(OriginalSymbol, tparam)
+ TypeDef(tparam.name, tbounds)
+ .withFlags(Param | PrivateLocal | ecTparam.flags & VarianceFlags).withPos(pos)
+ }
+ }
+
+ /** A reference to the enum class `E`, possibly followed by type arguments.
+ * Each covariant type parameter is approximated by its lower bound.
+ * Each contravariant type parameter is approximated by its upper bound.
+ * It is an error if a type parameter is non-variant, or if its approximation
+ * refers to pther type parameters.
+ */
+ def interpolatedEnumParent(pos: Position)(implicit ctx: Context): Tree = {
+ val tparams = enumClass.typeParams
+ def isGround(tp: Type) = tp.subst(tparams, tparams.map(_ => NoType)) eq tp
+ val targs = tparams map { tparam =>
+ if (tparam.variance > 0 && isGround(tparam.info.bounds.lo))
+ tparam.info.bounds.lo
+ else if (tparam.variance < 0 && isGround(tparam.info.bounds.hi))
+ tparam.info.bounds.hi
+ else {
+ def problem =
+ if (tparam.variance == 0) "is non variant"
+ else "has bounds that depend on a type parameter in the same parameter list"
+ errorType(i"""cannot determine type argument for enum parent $enumClass,
+ |type parameter $tparam $problem""", pos)
+ }
+ }
+ TypeTree(enumClass.typeRef.appliedTo(targs)).withPos(pos)
+ }
+
+ /** A type tree referring to `enumClass` */
+ def enumClassRef(implicit ctx: Context) = TypeTree(enumClass.typeRef)
+
+ /** Add implied flags to an enum class or an enum case */
+ def addEnumFlags(cdef: TypeDef)(implicit ctx: Context) =
+ if (cdef.mods.hasMod[Mod.Enum]) cdef.withFlags(cdef.mods.flags | Abstract | Sealed)
+ else if (isLegalEnumCase(cdef)) cdef.withFlags(cdef.mods.flags | Final)
+ else cdef
+
+ private def valuesDot(name: String) = Select(Ident(nme.DOLLAR_VALUES), name.toTermName)
+ private def registerCall(implicit ctx: Context): List[Tree] =
+ if (enumClass.typeParams.nonEmpty) Nil
+ else Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil) :: Nil
+
+ /** The following lists of definitions for an enum type E:
+ *
+ * private val $values = new EnumValues[E]
+ * def enumValue = $values.fromInt
+ * def enumValueNamed = $values.fromName
+ * def enumValues = $values.values
+ */
+ private def enumScaffolding(implicit ctx: Context): List[Tree] = {
+ def enumDefDef(name: String, select: String) =
+ DefDef(name.toTermName, Nil, Nil, TypeTree(), valuesDot(select))
+ val privateValuesDef =
+ ValDef(nme.DOLLAR_VALUES, TypeTree(),
+ New(TypeTree(defn.EnumValuesType.appliedTo(enumClass.typeRef :: Nil)), ListOfNil))
+ .withFlags(Private)
+ val valueOfDef = enumDefDef("enumValue", "fromInt")
+ val withNameDef = enumDefDef("enumValueNamed", "fromName")
+ val valuesDef = enumDefDef("enumValues", "values")
+ List(privateValuesDef, valueOfDef, withNameDef, valuesDef)
+ }
+
+ /** A creation method for a value of enum type `E`, which is defined as follows:
+ *
+ * private def $new(tag: Int, name: String) = new E {
+ * def enumTag = tag
+ * override def toString = name
+ * $values.register(this)
+ * }
+ */
+ private def enumValueCreator(implicit ctx: Context) = {
+ def param(name: TermName, typ: Type) =
+ ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param)
+ val enumTagDef =
+ DefDef(nme.enumTag, Nil, Nil, TypeTree(), Ident(nme.tag))
+ val toStringDef =
+ DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name))
+ .withFlags(Override)
+ def creator = New(Template(emptyConstructor, enumClassRef :: Nil, EmptyValDef,
+ List(enumTagDef, toStringDef) ++ registerCall))
+ DefDef(nme.DOLLAR_NEW, Nil,
+ List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))),
+ TypeTree(), creator)
+ }
+
+ /** A pair consisting of
+ * - the next enum tag
+ * - scaffolding containing the necessary definitions for singleton enum cases
+ * unless that scaffolding was already generated by a previous call to `nextEnumKind`.
+ */
+ def nextEnumTag(kind: CaseKind.Value)(implicit ctx: Context): (Int, List[Tree]) = {
+ val (count, seenKind) = ctx.tree.removeAttachment(EnumCaseCount).getOrElse((0, CaseKind.Class))
+ val minKind = if (kind < seenKind) kind else seenKind
+ ctx.tree.pushAttachment(EnumCaseCount, (count + 1, minKind))
+ val scaffolding =
+ if (enumClass.typeParams.nonEmpty || kind >= seenKind) Nil
+ else if (kind == CaseKind.Object) enumScaffolding
+ else if (seenKind == CaseKind.Object) enumValueCreator :: Nil
+ else enumScaffolding :+ enumValueCreator
+ (count, scaffolding)
+ }
+
+ /** A pair consisting of
+ * - a method returning the next enum tag
+ * - scaffolding as defined in `nextEnumTag`
+ */
+ def enumTagMeth(kind: CaseKind.Value)(implicit ctx: Context): (DefDef, List[Tree]) = {
+ val (tag, scaffolding) = nextEnumTag(kind)
+ (DefDef(nme.enumTag, Nil, Nil, TypeTree(), Literal(Constant(tag))), scaffolding)
+ }
+
+ /** Expand a module definition representing a parameterless enum case */
+ def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree =
+ if (impl.parents.isEmpty)
+ if (impl.body.isEmpty)
+ expandSimpleEnumCase(name, mods, pos)
+ else {
+ val parent = interpolatedEnumParent(pos)
+ expandEnumModule(name, cpy.Template(impl)(parents = parent :: Nil), mods, pos)
+ }
+ else {
+ def toStringMeth =
+ DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString)))
+ .withFlags(Override)
+ val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object)
+ val impl1 = cpy.Template(impl)(body =
+ impl.body ++ List(tagMeth, toStringMeth) ++ registerCall)
+ val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final).withPos(pos)
+ flatTree(scaffolding ::: vdef :: Nil).withPos(pos.startPos)
+ }
+
+ /** Expand a simple enum case */
+ def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree =
+ if (enumClass.typeParams.nonEmpty) {
+ val parent = interpolatedEnumParent(pos)
+ val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, Nil)
+ expandEnumModule(name, impl, mods, pos)
+ }
+ else {
+ val (tag, scaffolding) = nextEnumTag(CaseKind.Simple)
+ val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString))))
+ val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final).withPos(pos)
+ flatTree(scaffolding ::: vdef :: Nil).withPos(pos.startPos)
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala
index 1f37ad1c5..a5feecc77 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 {
@@ -132,6 +133,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class Inline() extends Mod(Flags.Inline)
case class Type() extends Mod(Flags.EmptyFlags)
+
+ case class Enum() extends Mod(Flags.EmptyFlags)
+
+ case class EnumCase() extends Mod(Flags.EmptyFlags)
}
/** Modifiers and annotations for definitions
@@ -185,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/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala
index 6b7be12be..8707b66f9 100644
--- a/compiler/src/dotty/tools/dotc/core/Contexts.scala
+++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala
@@ -306,13 +306,14 @@ object Contexts {
sb.append(str)
}
- /** The next outer context whose tree is a template or package definition */
+ /** The next outer context whose tree is a template or package definition
+ * Note: Currently unused
def enclTemplate: Context = {
var c = this
while (c != NoContext && !c.tree.isInstanceOf[Template[_]] && !c.tree.isInstanceOf[PackageDef[_]])
c = c.outer
c
- }
+ }*/
/** The context for a supercall. This context is used for elaborating
* the parents of a class and their arguments.
diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala
index 0e8ae196a..c65dc5b97 100644
--- a/compiler/src/dotty/tools/dotc/core/Decorators.scala
+++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala
@@ -185,7 +185,7 @@ object Decorators {
* give more info about type variables and to disambiguate where needed.
*/
def ex(args: Any*)(implicit ctx: Context): String =
- explained2(implicit ctx => em(args: _*))
+ explained(implicit ctx => em(args: _*))
/** Formatter that adds syntax highlighting to all interpolated values */
def hl(args: Any*)(implicit ctx: Context): String =
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index 4d4350f98..39b46cbfe 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -512,6 +512,10 @@ class Definitions {
def DynamicClass(implicit ctx: Context) = DynamicType.symbol.asClass
lazy val OptionType: TypeRef = ctx.requiredClassRef("scala.Option")
def OptionClass(implicit ctx: Context) = OptionType.symbol.asClass
+ lazy val EnumType: TypeRef = ctx.requiredClassRef("scala.Enum")
+ def EnumClass(implicit ctx: Context) = EnumType.symbol.asClass
+ lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues")
+ def EnumValuesClass(implicit ctx: Context) = EnumValuesType.symbol.asClass
lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product")
def ProductClass(implicit ctx: Context) = ProductType.symbol.asClass
lazy val Product_canEqualR = ProductClass.requiredMethodRef(nme.canEqual_)
diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala
index c1267d8a2..29f1078a2 100644
--- a/compiler/src/dotty/tools/dotc/core/Flags.scala
+++ b/compiler/src/dotty/tools/dotc/core/Flags.scala
@@ -450,13 +450,22 @@ object Flags {
/** Flags guaranteed to be set upon symbol creation */
final val FromStartFlags =
- AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon |
- Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed |
- CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic |
- Inline | LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass
+ Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor |
+ Scala2ExistentialCommon | Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic |
+ CovariantOrOuter | ContravariantOrLabel | ExpandedName | CaseAccessorOrBaseTypeArg |
+ Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic |
+ SuperAccessorOrScala2x | Inline
+
+ /** Flags guaranteed to be set upon symbol creation, or, if symbol is a top-level
+ * class or object, when the class file defining the symbol is loaded (which
+ * is generally before the symbol is completed
+ */
+ final val AfterLoadFlags =
+ FromStartFlags | AccessFlags | Final | AccessorOrSealed | LazyOrTrait | SelfNameOrImplClass
assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags)
// TODO: Should check that FromStartFlags do not change in completion
+ assert(AfterLoadFlags.isTermFlags && AfterLoadFlags.isTypeFlags)
/** A value that's unstable unless complemented with a Stable flag */
final val UnstableValue = Mutable | Method
diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala
index 5b7dc3d1d..bc3f96d91 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -132,6 +132,8 @@ object StdNames {
val TRAIT_SETTER_SEPARATOR: N = "$_setter_$"
val DIRECT_SUFFIX: N = "$direct"
val LAZY_IMPLICIT_PREFIX: N = "$lazy_implicit$"
+ val DOLLAR_VALUES: N = "$values"
+ val DOLLAR_NEW: N = "$new"
// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
@@ -395,6 +397,7 @@ object StdNames {
val elem: N = "elem"
val emptyValDef: N = "emptyValDef"
val ensureAccessible : N = "ensureAccessible"
+ val enumTag: N = "enumTag"
val eq: N = "eq"
val equalsNumChar : N = "equalsNumChar"
val equalsNumNum : N = "equalsNumNum"
@@ -499,6 +502,7 @@ object StdNames {
val staticModule : N = "staticModule"
val staticPackage : N = "staticPackage"
val synchronized_ : N = "synchronized"
+ val tag: N = "tag"
val tail: N = "tail"
val `then` : N = "then"
val this_ : N = "this"
@@ -523,7 +527,7 @@ object StdNames {
val updateDynamic: N = "updateDynamic"
val value: N = "value"
val valueOf : N = "valueOf"
- val values : N = "values"
+ val values: N = "values"
val view_ : N = "view"
val wait_ : N = "wait"
val withFilter: N = "withFilter"
diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala
index db96463e0..27782698d 100644
--- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -163,26 +163,31 @@ object SymDenotations {
setFlag(flags & mask)
}
+ private def isCurrent(fs: FlagSet) =
+ fs <= (
+ if (myInfo.isInstanceOf[SymbolLoader]) FromStartFlags
+ else AfterLoadFlags)
+
/** Has this denotation one of the flags in `fs` set? */
final def is(fs: FlagSet)(implicit ctx: Context) = {
- (if (fs <= FromStartFlags) myFlags else flags) is fs
+ (if (isCurrent(fs)) myFlags else flags) is fs
}
/** Has this denotation one of the flags in `fs` set, whereas none of the flags
* in `butNot` are set?
*/
final def is(fs: FlagSet, butNot: FlagSet)(implicit ctx: Context) =
- (if (fs <= FromStartFlags && butNot <= FromStartFlags) myFlags else flags) is (fs, butNot)
+ (if (isCurrent(fs) && isCurrent(butNot)) myFlags else flags) is (fs, butNot)
/** Has this denotation all of the flags in `fs` set? */
final def is(fs: FlagConjunction)(implicit ctx: Context) =
- (if (fs <= FromStartFlags) myFlags else flags) is fs
+ (if (isCurrent(fs)) myFlags else flags) is fs
/** Has this denotation all of the flags in `fs` set, whereas none of the flags
* in `butNot` are set?
*/
final def is(fs: FlagConjunction, butNot: FlagSet)(implicit ctx: Context) =
- (if (fs <= FromStartFlags && butNot <= FromStartFlags) myFlags else flags) is (fs, butNot)
+ (if (isCurrent(fs) && isCurrent(butNot)) myFlags else flags) is (fs, butNot)
/** The type info.
* The info is an instance of TypeType iff this is a type denotation
diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
index 6b73a9456..47f201a09 100644
--- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
+++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
@@ -828,11 +828,11 @@ object JavaParsers {
val superclazz = Apply(TypeApply(
Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)),
List(Literal(Constant(null)),Literal(Constant(0))))
- val enum = atPos(start, nameOffset) {
+ val enumclazz = atPos(start, nameOffset) {
TypeDef(name,
makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.Enum)
}
- addCompanionObject(consts ::: statics ::: predefs, enum)
+ addCompanionObject(consts ::: statics ::: predefs, enumclazz)
}
def enumConst(enumType: Tree) = {
diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala
index b644c94cc..9864281a5 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.
*/
@@ -1873,29 +1880,32 @@ object Parsers {
mods1
}
- /** Def ::= val PatDef
- * | var VarDef
- * | def DefDef
- * | type {nl} TypeDcl
- * | TmplDef
- * Dcl ::= val ValDcl
- * | var ValDcl
- * | def DefDcl
- * | type {nl} TypeDcl
+ /** Def ::= val PatDef
+ * | var VarDef
+ * | def DefDef
+ * | type {nl} TypeDcl
+ * | TmplDef
+ * Dcl ::= val ValDcl
+ * | var ValDcl
+ * | def DefDcl
+ * | type {nl} TypeDcl
+ * EnumCase ::= `case' (EnumClassDef | ObjectDef)
*/
def defOrDcl(start: Int, mods: Modifiers): Tree = in.token match {
case VAL =>
val mod = atPos(in.skipToken()) { Mod.Val() }
val mods1 = mods.withAddedMod(mod)
- patDefOrDcl(start, mods1, in.getDocComment(start))
+ patDefOrDcl(start, mods1)
case VAR =>
val mod = atPos(in.skipToken()) { Mod.Var() }
val mod1 = addMod(mods, mod)
- patDefOrDcl(start, mod1, in.getDocComment(start))
+ patDefOrDcl(start, mod1)
case DEF =>
- defDefOrDcl(start, posMods(start, mods), in.getDocComment(start))
+ defDefOrDcl(start, posMods(start, mods))
case TYPE =>
- typeDefOrDcl(start, posMods(start, mods), in.getDocComment(start))
+ typeDefOrDcl(start, posMods(start, mods))
+ case CASE =>
+ enumCase(start, mods)
case _ =>
tmplDef(start, mods)
}
@@ -1905,7 +1915,7 @@ object Parsers {
* ValDcl ::= id {`,' id} `:' Type
* VarDcl ::= id {`,' id} `:' Type
*/
- def patDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = atPos(start, nameStart) {
+ def patDefOrDcl(start: Offset, mods: Modifiers): Tree = atPos(start, nameStart) {
val lhs = commaSeparated(pattern2)
val tpt = typedOpt()
val rhs =
@@ -1920,7 +1930,7 @@ object Parsers {
} else EmptyTree
lhs match {
case (id @ Ident(name: TermName)) :: Nil => {
- ValDef(name, tpt, rhs).withMods(mods).setComment(docstring)
+ ValDef(name, tpt, rhs).withMods(mods).setComment(in.getDocComment(start))
} case _ =>
PatDef(mods, lhs, tpt, rhs)
}
@@ -1946,7 +1956,7 @@ object Parsers {
* DefDcl ::= DefSig `:' Type
* DefSig ::= id [DefTypeParamClause] ParamClauses
*/
- def defDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = atPos(start, nameStart) {
+ def defDefOrDcl(start: Offset, mods: Modifiers): Tree = atPos(start, nameStart) {
def scala2ProcedureSyntax(resultTypeStr: String) = {
val toInsert =
if (in.token == LBRACE) s"$resultTypeStr ="
@@ -1989,7 +1999,7 @@ object Parsers {
accept(EQUALS)
expr()
}
- DefDef(name, tparams, vparamss, tpt, rhs).withMods(mods1).setComment(docstring)
+ DefDef(name, tparams, vparamss, tpt, rhs).withMods(mods1).setComment(in.getDocComment(start))
}
}
@@ -2023,7 +2033,7 @@ object Parsers {
/** TypeDef ::= type id [TypeParamClause] `=' Type
* TypeDcl ::= type id [TypeParamClause] TypeBounds
*/
- def typeDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = {
+ def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = {
newLinesOpt()
atPos(start, nameStart) {
val name = ident().toTypeName
@@ -2031,9 +2041,9 @@ object Parsers {
in.token match {
case EQUALS =>
in.nextToken()
- TypeDef(name, lambdaAbstract(tparams, typ())).withMods(mods).setComment(docstring)
+ TypeDef(name, lambdaAbstract(tparams, typ())).withMods(mods).setComment(in.getDocComment(start))
case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | EOF =>
- TypeDef(name, lambdaAbstract(tparams, typeBounds())).withMods(mods).setComment(docstring)
+ TypeDef(name, lambdaAbstract(tparams, typeBounds())).withMods(mods).setComment(in.getDocComment(start))
case _ =>
syntaxErrorOrIncomplete("`=', `>:', or `<:' expected")
EmptyTree
@@ -2041,43 +2051,51 @@ object Parsers {
}
}
- /** TmplDef ::= ([`case'] `class' | `trait') ClassDef
+ /** TmplDef ::= ([`case' | `enum]'] ‘class’ | trait’) ClassDef
* | [`case'] `object' ObjectDef
+ * | `enum' EnumDef
*/
def tmplDef(start: Int, mods: Modifiers): Tree = {
- val docstring = in.getDocComment(start)
in.token match {
case TRAIT =>
- classDef(start, posMods(start, addFlag(mods, Trait)), docstring)
+ classDef(start, posMods(start, addFlag(mods, Trait)))
case CLASS =>
- classDef(start, posMods(start, mods), docstring)
+ classDef(start, posMods(start, mods))
case CASECLASS =>
- classDef(start, posMods(start, mods | Case), docstring)
+ classDef(start, posMods(start, mods | Case))
case OBJECT =>
- objectDef(start, posMods(start, mods | Module), docstring)
+ objectDef(start, posMods(start, mods | Module))
case CASEOBJECT =>
- objectDef(start, posMods(start, mods | Case | Module), docstring)
+ objectDef(start, posMods(start, mods | Case | Module))
+ case ENUM =>
+ 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
}
}
- /** ClassDef ::= id [ClsTypeParamClause]
- * [ConstrMods] ClsParamClauses TemplateOpt
+ /** ClassDef ::= id ClassConstr TemplateOpt
*/
- def classDef(start: Offset, mods: Modifiers, docstring: Option[Comment]): TypeDef = atPos(start, nameStart) {
- val name = ident().toTypeName
- val constr = atPos(in.lastOffset) {
- val tparams = typeParamClauseOpt(ParamOwner.Class)
- val cmods = constrModsOpt(name)
- val vparamss = paramClauses(name, mods is Case)
+ def classDef(start: Offset, mods: Modifiers): TypeDef = atPos(start, nameStart) {
+ classDefRest(start, mods, ident().toTypeName)
+ }
- makeConstructor(tparams, vparamss).withMods(cmods)
- }
+ def classDefRest(start: Offset, mods: Modifiers, name: TypeName): TypeDef = {
+ val constr = classConstr(name, isCaseClass = mods is Case)
val templ = templateOpt(constr)
+ TypeDef(name, templ).withMods(mods).setComment(in.getDocComment(start))
+ }
- TypeDef(name, templ).withMods(mods).setComment(docstring)
+ /** ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses
+ */
+ def classConstr(owner: Name, isCaseClass: Boolean = false): DefDef = atPos(in.lastOffset) {
+ val tparams = typeParamClauseOpt(ParamOwner.Class)
+ val cmods = constrModsOpt(owner)
+ val vparamss = paramClauses(owner, isCaseClass)
+ makeConstructor(tparams, vparamss).withMods(cmods)
}
/** ConstrMods ::= AccessModifier
@@ -2093,11 +2111,73 @@ object Parsers {
/** ObjectDef ::= id TemplateOpt
*/
- def objectDef(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): ModuleDef = atPos(start, nameStart) {
- val name = ident()
+ def objectDef(start: Offset, mods: Modifiers): ModuleDef = atPos(start, nameStart) {
+ objectDefRest(start, mods, ident())
+ }
+
+ def objectDefRest(start: Offset, mods: Modifiers, name: TermName): ModuleDef = {
val template = templateOpt(emptyConstructor)
+ ModuleDef(name, template).withMods(mods).setComment(in.getDocComment(start))
+ }
+
+ /** id ClassConstr [`extends' [ConstrApps]]
+ * [nl] ‘{’ EnumCaseStats ‘}’
+ */
+ 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();
+ newLineOptWhenFollowedBy(LBRACE)
+ 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 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 */
+ def enumCaseStats(): List[DefTree] = {
+ val cases = new ListBuffer[DefTree] += enumCaseStat()
+ while (in.token != RBRACE) {
+ acceptStatSep()
+ cases += enumCaseStat()
+ }
+ cases.toList
+ }
+
+ /** EnumCaseStat = {Annotation [nl]} {Modifier} EnumCase */
+ def enumCaseStat(): DefTree =
+ enumCase(in.offset, defAnnotsMods(modifierTokens))
- ModuleDef(name, template).withMods(mods).setComment(docstring)
+ /** EnumCase = `case' (EnumClassDef | ObjectDef) */
+ def enumCase(start: Offset, mods: Modifiers): DefTree = {
+ val mods1 = mods.withAddedMod(atPos(in.offset)(Mod.EnumCase())) | Case
+ accept(CASE)
+ atPos(start, nameStart) {
+ val id = termIdent()
+ if (in.token == LBRACKET || in.token == LPAREN)
+ classDefRest(start, mods1, id.name.toTypeName)
+ else if (in.token == COMMA) {
+ in.nextToken()
+ val ids = commaSeparated(termIdent)
+ PatDef(mods1, id :: ids, TypeTree(), EmptyTree)
+ }
+ else
+ objectDefRest(start, mods1, id.name.asTermName)
+ }
}
/* -------- TEMPLATES ------------------------------------------- */
@@ -2191,7 +2271,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")
@@ -2209,8 +2289,8 @@ object Parsers {
* TemplateStat ::= Import
* | Annotations Modifiers Def
* | Annotations Modifiers Dcl
+ * | EnumCaseStat
* | Expr1
- * | super ArgumentExprs {ArgumentExprs}
* |
*/
def templateStatSeq(): (ValDef, List[Tree]) = checkNoEscapingPlaceholders {
@@ -2240,8 +2320,8 @@ object Parsers {
stats ++= importClause()
else if (isExprIntro)
stats += expr1()
- else if (isDefIntro(modifierTokens))
- stats += defOrDcl(in.offset, defAnnotsMods(modifierTokens))
+ else if (isDefIntro(modifierTokensOrCase))
+ stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens))
else if (!isStatSep) {
exitOnError = mustStartStat
syntaxErrorOrIncomplete("illegal start of definition")
@@ -2299,9 +2379,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
@@ -2323,8 +2403,7 @@ object Parsers {
if (in.token == PACKAGE) {
in.nextToken()
if (in.token == OBJECT) {
- val docstring = in.getDocComment(start)
- ts += objectDef(start, atPos(start, in.skipToken()) { Modifiers(Package) }, docstring)
+ ts += objectDef(start, atPos(start, in.skipToken()) { Modifiers(Package) })
if (in.token != EOF) {
acceptStatSep()
ts ++= topStatSeq()
diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala
index 847f600c0..ff5019dc9 100644
--- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala
+++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala
@@ -10,6 +10,7 @@ import util.Chars._
import Tokens._
import scala.annotation.{ switch, tailrec }
import scala.collection.mutable
+import scala.collection.immutable.SortedMap
import mutable.ListBuffer
import Utility.isNameStart
import rewrite.Rewrites.patch
@@ -174,37 +175,24 @@ object Scanners {
class Scanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends ScannerCommon(source)(ctx) {
val keepComments = ctx.settings.YkeepComments.value
- /** All doc comments as encountered, each list contains doc comments from
- * the same block level. Starting with the deepest level and going upward
- */
- private[this] var docsPerBlockStack: List[List[Comment]] = List(Nil)
-
- /** Adds level of nesting to docstrings */
- def enterBlock(): Unit =
- docsPerBlockStack = List(Nil) ::: docsPerBlockStack
-
- /** Removes level of nesting for docstrings */
- def exitBlock(): Unit = docsPerBlockStack = docsPerBlockStack match {
- case x :: Nil => List(Nil)
- case _ => docsPerBlockStack.tail
+ /** All doc comments kept by their end position in a `Map` */
+ private[this] var docstringMap: SortedMap[Int, Comment] = SortedMap.empty
+
+ private[this] def addComment(comment: Comment): Unit = {
+ val lookahead = lookaheadReader
+ def nextPos: Int = (lookahead.getc(): @switch) match {
+ case ' ' | '\t' => nextPos
+ case CR | LF | FF =>
+ // if we encounter line delimitng whitespace we don't count it, since
+ // it seems not to affect positions in source
+ nextPos - 1
+ case _ => lookahead.charOffset - 1
+ }
+ docstringMap = docstringMap + (nextPos -> comment)
}
/** Returns the closest docstring preceding the position supplied */
- def getDocComment(pos: Int): Option[Comment] = {
- def closest(c: Comment, docstrings: List[Comment]): Comment = docstrings match {
- case x :: xs if (c.pos.end < x.pos.end && x.pos.end <= pos) => closest(x, xs)
- case Nil => c
- }
-
- docsPerBlockStack match {
- case (list @ (x :: xs)) :: _ => {
- val c = closest(x, xs)
- docsPerBlockStack = list.dropWhile(_ != c).tail :: docsPerBlockStack.tail
- Some(c)
- }
- case _ => None
- }
- }
+ def getDocComment(pos: Int): Option[Comment] = docstringMap.get(pos)
/** A buffer for comments */
val commentBuf = new StringBuilder
@@ -294,7 +282,10 @@ object Scanners {
if (!sepRegions.isEmpty && sepRegions.head == lastToken)
sepRegions = sepRegions.tail
case ARROW =>
- if (!sepRegions.isEmpty && sepRegions.head == lastToken)
+ if (!sepRegions.isEmpty && sepRegions.head == ARROW)
+ sepRegions = sepRegions.tail
+ case EXTENDS =>
+ if (!sepRegions.isEmpty && sepRegions.head == ARROW)
sepRegions = sepRegions.tail
case STRINGLIT =>
if (inMultiLineInterpolation)
@@ -330,7 +321,8 @@ object Scanners {
if (isAfterLineEnd() &&
(canEndStatTokens contains lastToken) &&
(canStartStatTokens contains token) &&
- (sepRegions.isEmpty || sepRegions.head == RBRACE)) {
+ (sepRegions.isEmpty || sepRegions.head == RBRACE ||
+ sepRegions.head == ARROW && token == CASE)) {
next copyFrom this
// todo: make offset line-end of previous line?
offset = if (lineStartOffset <= offset) lineStartOffset else lastLineStartOffset
@@ -535,13 +527,13 @@ object Scanners {
case ',' =>
nextChar(); token = COMMA
case '(' =>
- enterBlock(); nextChar(); token = LPAREN
+ nextChar(); token = LPAREN
case '{' =>
- enterBlock(); nextChar(); token = LBRACE
+ nextChar(); token = LBRACE
case ')' =>
- exitBlock(); nextChar(); token = RPAREN
+ nextChar(); token = RPAREN
case '}' =>
- exitBlock(); nextChar(); token = RBRACE
+ nextChar(); token = RBRACE
case '[' =>
nextChar(); token = LBRACKET
case ']' =>
@@ -606,11 +598,12 @@ object Scanners {
val start = lastCharOffset
def finishComment(): Boolean = {
if (keepComments) {
- val pos = Position(start, charOffset, start)
+ val pos = Position(start, charOffset - 1, start)
val comment = Comment(pos, flushBuf(commentBuf))
- if (comment.isDocComment)
- docsPerBlockStack = (docsPerBlockStack.head :+ comment) :: docsPerBlockStack.tail
+ if (comment.isDocComment) {
+ addComment(comment)
+ }
}
true
diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala
index 8d42e525a..d2ea9240c 100644
--- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala
+++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala
@@ -92,6 +92,7 @@ abstract class TokensCommon {
//final val THEN = 60; enter(THEN, "then")
//final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate
//final val INLINE = 62; enter(INLINE, "inline")
+ //final val ENUM = 63; enter(ENUM, "enum")
/** special symbols */
final val COMMA = 70; enter(COMMA, "','")
@@ -175,6 +176,7 @@ object Tokens extends TokensCommon {
final val THEN = 60; enter(THEN, "then")
final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate
final val INLINE = 62; enter(INLINE, "inline")
+ final val ENUM = 63; enter(ENUM, "enum")
/** special symbols */
final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line")
@@ -192,7 +194,7 @@ object Tokens extends TokensCommon {
/** XML mode */
final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
- final val alphaKeywords = tokenRange(IF, INLINE)
+ final val alphaKeywords = tokenRange(IF, ENUM)
final val symbolicKeywords = tokenRange(USCORE, VIEWBOUND)
final val symbolicTokens = tokenRange(COMMA, VIEWBOUND)
final val keywords = alphaKeywords | symbolicKeywords
@@ -213,7 +215,7 @@ object Tokens extends TokensCommon {
final val canStartBindingTokens = identifierTokens | BitSet(USCORE, LPAREN)
- final val templateIntroTokens = BitSet(CLASS, TRAIT, OBJECT, CASECLASS, CASEOBJECT)
+ final val templateIntroTokens = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT)
final val dclIntroTokens = BitSet(DEF, VAL, VAR, TYPE)
@@ -228,6 +230,8 @@ object Tokens extends TokensCommon {
final val modifierTokens = localModifierTokens | accessModifierTokens | BitSet(
OVERRIDE)
+ final val modifierTokensOrCase = modifierTokens | BitSet(CASE)
+
/** Is token only legal as start of statement (eof also included)? */
final val mustStartStatTokens = defIntroTokens | modifierTokens | BitSet(
IMPORT, PACKAGE)
diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala
index 05f1af9d7..760b22689 100644
--- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala
+++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala
@@ -213,9 +213,11 @@ object Formatting {
* ex"disambiguate $tpe1 and $tpe2"
* ```
*/
- def explained2(op: Context => String)(implicit ctx: Context): String = {
+ def explained(op: Context => String)(implicit ctx: Context): String = {
val seen = new Seen
- op(explainCtx(seen)) ++ explanations(seen)
+ val msg = op(explainCtx(seen))
+ val addendum = explanations(seen)
+ if (addendum.isEmpty) msg else msg ++ "\n\n" ++ addendum
}
/** When getting a type mismatch it is useful to disambiguate placeholders like:
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
index 57365658e..b0f5f8ab1 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -238,7 +238,7 @@ object messages {
import core.Flags._
val maxDist = 3
val decls = site.decls.flatMap { sym =>
- if (sym.is(Synthetic | PrivateOrLocal) || sym.isConstructor) Nil
+ if (sym.flagsUNSAFE.is(Synthetic | PrivateOrLocal) || sym.isConstructor) Nil
else List((sym.name.show, sym))
}
diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala
index 5dcf16b62..310121f31 100644
--- a/compiler/src/dotty/tools/dotc/typer/Applications.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala
@@ -217,7 +217,14 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
// apply the result type constraint, unless method type is dependent
val resultApprox = resultTypeApprox(methType)
val savedConstraint = ctx.typerState.constraint
- if (!constrainResult(resultApprox, resultType))
+ if (!resultApprox.isInstanceOf[PolyType] &&
+ // temporary fix before #2121 is in. The problem here is that errors in the code
+ // can lead to the result type begin higher-kinded. Then normalize gets confused
+ // and we end up with an assertion violation "s"inconsistent: no typevars were
+ // added to committable constraint". Once we distinguish between type lambdas
+ // and polytypes again this should hopefully become unnecessary. The error
+ // was triggered by neg/enums.scala.
+ !constrainResult(resultApprox, resultType))
if (ctx.typerState.isCommittable)
// defer the problem until after the application;
// it might be healed by an implicit conversion
diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala
index 94506f318..ed580c631 100644
--- a/compiler/src/dotty/tools/dotc/typer/Namer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala
@@ -564,7 +564,6 @@ class Namer { typer: Typer =>
case _ =>
}
case _ =>
-
}
}
diff --git a/compiler/test/dotty/tools/dotc/parsing/DocstringTests.scala b/compiler/test/dotty/tools/dotc/parsing/DocstringTests.scala
index 930ec117a..81ac77761 100644
--- a/compiler/test/dotty/tools/dotc/parsing/DocstringTests.scala
+++ b/compiler/test/dotty/tools/dotc/parsing/DocstringTests.scala
@@ -488,4 +488,34 @@ class DocstringTests extends DocstringTest {
checkDocString(c.rawComment.map(_.raw), "/** Class1 */")
}
}
+
+ @Test def nestedComment = {
+ val source =
+ """
+ |trait T {
+ | /** Cheeky comment */
+ |}
+ |class C
+ """.stripMargin
+
+ import dotty.tools.dotc.ast.untpd._
+ checkFrontend(source) {
+ case p @ PackageDef(_, Seq(_, c: TypeDef)) =>
+ assert(c.rawComment == None, s"class C is not supposed to have a docstring (${c.rawComment.get}) in:$source")
+ }
+ }
+
+ @Test def eofComment = {
+ val source =
+ """
+ |class C
+ |/** Cheeky comment */
+ """.stripMargin
+
+ import dotty.tools.dotc.ast.untpd._
+ checkFrontend(source) {
+ case p @ PackageDef(_, Seq(c: TypeDef)) =>
+ assert(c.rawComment == None, s"class C is not supposed to have a docstring (${c.rawComment.get}) in:$source")
+ }
+ }
} /* End class */
diff --git a/compiler/test/dotty/tools/dotc/repl/TestREPL.scala b/compiler/test/dotty/tools/dotc/repl/TestREPL.scala
index 131a88ab1..c5557b86e 100644
--- a/compiler/test/dotty/tools/dotc/repl/TestREPL.scala
+++ b/compiler/test/dotty/tools/dotc/repl/TestREPL.scala
@@ -4,7 +4,7 @@ package repl
import core.Contexts.Context
import collection.mutable
-import java.io.StringWriter
+import java.io.{StringWriter, PrintStream}
import dotty.tools.io.{ PlainFile, Directory }
import org.junit.Test
@@ -58,8 +58,11 @@ class TestREPL(script: String) extends REPL {
val printed = out.toString
val transcript = printed.drop(printed.indexOf(config.prompt))
if (transcript.toString.lines.toList != script.lines.toList) {
- println("input differs from transcript:")
+ println("input differs from transcript (copy is repl.transcript):")
println(transcript)
+ val s = new PrintStream("repl.transcript")
+ s.print(transcript)
+ s.close()
assert(false)
}
}
diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md
index 7f06cdc2a..e4285e20f 100644
--- a/docs/docs/internals/syntax.md
+++ b/docs/docs/internals/syntax.md
@@ -289,7 +289,9 @@ TemplateBody ::= [nl] ‘{’ [SelfType] TemplateStat {semi TemplateStat}
TemplateStat ::= Import
| {Annotation [nl]} {Modifier} Def
| {Annotation [nl]} {Modifier} Dcl
+ | EnumCaseStat
| Expr1
+ |
SelfType ::= id [‘:’ InfixType] ‘=>’ ValDef(_, name, tpt, _)
| ‘this’ ‘:’ InfixType ‘=>’
@@ -328,13 +330,20 @@ DefDef ::= DefSig [‘:’ Type] ‘=’ Expr
| ‘this’ DefParamClause DefParamClauses DefDef(_, <init>, Nil, vparamss, EmptyTree, expr | Block)
(‘=’ ConstrExpr | [nl] ConstrBlock)
-TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
+TmplDef ::= ([‘case’ | `enum'] ‘class’ | trait’) ClassDef
| [‘case’] ‘object’ ObjectDef
-ClassDef ::= id [ClsTypeParamClause] ClassDef(mods, name, tparams, templ)
- [ConstrMods] ClsParamClauses TemplateOpt with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat
+ | `enum' EnumDef
+ClassDef ::= id ClassConstr TemplateOpt ClassDef(mods, name, tparams, templ)
+ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat
ConstrMods ::= AccessModifier
| Annotation {Annotation} (AccessModifier | ‘this’)
ObjectDef ::= id TemplateOpt ModuleDef(mods, name, template) // no constructor
+EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumDef(mods, name, tparams, template)
+ [nl] ‘{’ EnumCaseStat {semi EnumCaseStat} ‘}’
+EnumCaseStat ::= {Annotation [nl]} {Modifier} EnumCase
+EnumCase ::= `case' (EnumClassDef | ObjectDef | ids)
+EnumClassDef ::= id [ClsTpeParamClause | ClsParamClause] ClassDef(mods, name, tparams, templ)
+ ClsParamClauses TemplateOpt
TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody]
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
ConstrApps ::= ConstrApp {‘with’ ConstrApp}
diff --git a/library/src/scala/Enum.scala b/library/src/scala/Enum.scala
new file mode 100644
index 000000000..7d2eefb3d
--- /dev/null
+++ b/library/src/scala/Enum.scala
@@ -0,0 +1,8 @@
+package scala
+
+/** A base trait of all enum classes */
+trait Enum {
+
+ /** A number uniquely identifying a case of an enum */
+ def enumTag: Int
+}
diff --git a/library/src/scala/runtime/EnumValues.scala b/library/src/scala/runtime/EnumValues.scala
new file mode 100644
index 000000000..6f9d907b3
--- /dev/null
+++ b/library/src/scala/runtime/EnumValues.scala
@@ -0,0 +1,21 @@
+package scala.runtime
+
+import scala.collection.immutable.Map
+
+class EnumValues[E <: Enum] {
+ private var myMap: Map[Int, E] = Map()
+ private var fromNameCache: Map[String, E] = null
+
+ def register(v: E) = {
+ require(!myMap.contains(v.enumTag))
+ myMap = myMap.updated(v.enumTag, v)
+ fromNameCache = null
+ }
+
+ def fromInt: Map[Int, E] = myMap
+ def fromName: Map[String, E] = {
+ if (fromNameCache == null) fromNameCache = myMap.values.map(v => v.toString -> v).toMap
+ fromNameCache
+ }
+ def values: Iterable[E] = myMap.values
+}
diff --git a/tests/neg/enums.scala b/tests/neg/enums.scala
new file mode 100644
index 000000000..108ec4a6c
--- /dev/null
+++ b/tests/neg/enums.scala
@@ -0,0 +1,20 @@
+enum List[+T] {
+ case Cons(x: T, xs: List[T])
+ case Snoc[U](xs: List[U], x: U) // error: case with type parameters needs extends clause
+}
+
+enum class X {
+ case Y // error: case not allowed here
+}
+
+enum E1[T] {
+ case C // error: cannot determine type argument
+}
+
+enum E2[+T, +U >: T] {
+ case C // error: cannot determine type argument
+}
+
+enum E3[-T <: Ordered[T]] {
+ case C // error: cannot determine type argument
+}
diff --git a/tests/pos/enum-List-control.scala b/tests/pos/enum-List-control.scala
new file mode 100644
index 000000000..d9df176d1
--- /dev/null
+++ b/tests/pos/enum-List-control.scala
@@ -0,0 +1,14 @@
+abstract sealed class List[T] extends Enum
+object List {
+ final case class Cons[T](x: T, xs: List[T]) extends List[T] {
+ def enumTag = 0
+ }
+ final case class Nil[T]() extends List[T] {
+ def enumTag = 1
+ }
+}
+object Test {
+ import List._
+ val xs = Cons(1, Cons(2, Cons(3, Nil())))
+ def main(args: Array[String]) = println(xs)
+}
diff --git a/tests/run/enum-Color.check b/tests/run/enum-Color.check
new file mode 100644
index 000000000..865c47e49
--- /dev/null
+++ b/tests/run/enum-Color.check
@@ -0,0 +1,3 @@
+Red: 0
+Green: 1
+Blue: 2
diff --git a/tests/run/enum-Color.scala b/tests/run/enum-Color.scala
new file mode 100644
index 000000000..683d18d9e
--- /dev/null
+++ b/tests/run/enum-Color.scala
@@ -0,0 +1,11 @@
+enum Color {
+ case Red, Green, Blue
+}
+
+object Test {
+ def main(args: Array[String]) =
+ for (color <- Color.enumValues) {
+ println(s"$color: ${color.enumTag}")
+ assert(Color.enumValue(color.enumTag) eq color)
+ }
+}
diff --git a/tests/run/enum-HList.scala b/tests/run/enum-HList.scala
new file mode 100644
index 000000000..c019cb6cc
--- /dev/null
+++ b/tests/run/enum-HList.scala
@@ -0,0 +1,22 @@
+enum HLst {
+ case HCons[+Hd, +Tl <: HLst](hd: Hd, tl: Tl)
+ case HNil
+}
+
+object Test {
+ import HLst._
+ def length(hl: HLst): Int = hl match {
+ case HCons(_, tl) => 1 + length(tl)
+ case HNil => 0
+ }
+ def sumInts(hl: HLst): Int = hl match {
+ case HCons(x: Int, tl) => x + sumInts(tl)
+ case HCons(_, tl) => sumInts(tl)
+ case HNil => 0
+ }
+ def main(args: Array[String]) = {
+ val hl = HCons(1, HCons("A", HNil))
+ assert(length(hl) == 2, length(hl))
+ assert(sumInts(hl) == 1)
+ }
+}
diff --git a/tests/run/enum-List1.check b/tests/run/enum-List1.check
new file mode 100644
index 000000000..3ed5061b4
--- /dev/null
+++ b/tests/run/enum-List1.check
@@ -0,0 +1 @@
+Cons(1,Cons(2,Cons(3,Nil())))
diff --git a/tests/run/enum-List1.scala b/tests/run/enum-List1.scala
new file mode 100644
index 000000000..bb75bec4a
--- /dev/null
+++ b/tests/run/enum-List1.scala
@@ -0,0 +1,10 @@
+enum class List[T]
+object List {
+ case Cons(x: T, xs: List[T])
+ case Nil()
+}
+object Test {
+ import List._
+ val xs = Cons(1, Cons(2, Cons(3, Nil())))
+ def main(args: Array[String]) = println(xs)
+}
diff --git a/tests/run/enum-List2.check b/tests/run/enum-List2.check
new file mode 100644
index 000000000..1d4812de1
--- /dev/null
+++ b/tests/run/enum-List2.check
@@ -0,0 +1 @@
+Cons(1,Cons(2,Cons(3,Nil)))
diff --git a/tests/run/enum-List2.scala b/tests/run/enum-List2.scala
new file mode 100644
index 000000000..030de0f84
--- /dev/null
+++ b/tests/run/enum-List2.scala
@@ -0,0 +1,11 @@
+enum class List[+T]
+object List {
+ case Cons(x: T, xs: List[T])
+ case Nil extends List[Nothing]
+}
+object Test {
+ import List._
+ val xs = Cons(1, Cons(2, Cons(3, Nil)))
+ def main(args: Array[String]) = println(xs)
+}
+
diff --git a/tests/run/enum-List2a.check b/tests/run/enum-List2a.check
new file mode 100644
index 000000000..1d4812de1
--- /dev/null
+++ b/tests/run/enum-List2a.check
@@ -0,0 +1 @@
+Cons(1,Cons(2,Cons(3,Nil)))
diff --git a/tests/run/enum-List2a.scala b/tests/run/enum-List2a.scala
new file mode 100644
index 000000000..323a5587c
--- /dev/null
+++ b/tests/run/enum-List2a.scala
@@ -0,0 +1,11 @@
+enum class List[+T]
+object List {
+ case Cons(x: T, xs: List[T])
+ case Nil
+}
+object Test {
+ import List._
+ val xs = Cons(1, Cons(2, Cons(3, Nil)))
+ def main(args: Array[String]) = println(xs)
+}
+
diff --git a/tests/run/enum-List3.check b/tests/run/enum-List3.check
new file mode 100644
index 000000000..1d4812de1
--- /dev/null
+++ b/tests/run/enum-List3.check
@@ -0,0 +1 @@
+Cons(1,Cons(2,Cons(3,Nil)))
diff --git a/tests/run/enum-List3.scala b/tests/run/enum-List3.scala
new file mode 100644
index 000000000..e5ffe1a28
--- /dev/null
+++ b/tests/run/enum-List3.scala
@@ -0,0 +1,10 @@
+enum List[+T] {
+ case Cons(x: T, xs: List[T])
+ case Nil extends List[Nothing]
+}
+object Test {
+ import List._
+ val xs = Cons(1, Cons(2, Cons(3, Nil)))
+ def main(args: Array[String]) = println(xs)
+}
+
diff --git a/tests/run/enum-Option.scala b/tests/run/enum-Option.scala
new file mode 100644
index 000000000..76f5641b3
--- /dev/null
+++ b/tests/run/enum-Option.scala
@@ -0,0 +1,19 @@
+enum class Option[+T >: Null] extends Serializable {
+ def isDefined: Boolean
+}
+object Option {
+ def apply[T >: Null](x: T): Option[T] = if (x == null) None else Some(x)
+ case Some(x: T) {
+ def isDefined = true
+ }
+ case None {
+ def isDefined = false
+ }
+}
+
+object Test {
+ def main(args: Array[String]) = {
+ assert(Some(None).isDefined)
+ Option("22") match { case Option.Some(x) => assert(x == "22") }
+ }
+}
diff --git a/tests/run/enum-Tree.check b/tests/run/enum-Tree.check
new file mode 100644
index 000000000..02f5151be
--- /dev/null
+++ b/tests/run/enum-Tree.check
@@ -0,0 +1 @@
+If(IsZero(Pred(Succ(Zero))),Succ(Succ(Zero)),Pred(Pred(Zero))) --> 2
diff --git a/tests/run/enum-Tree.scala b/tests/run/enum-Tree.scala
new file mode 100644
index 000000000..ef5bd7a57
--- /dev/null
+++ b/tests/run/enum-Tree.scala
@@ -0,0 +1,29 @@
+enum Tree[T] {
+ case True extends Tree[Boolean]
+ case False extends Tree[Boolean]
+ case Zero extends Tree[Int]
+ case Succ(n: Tree[Int]) extends Tree[Int]
+ case Pred(n: Tree[Int]) extends Tree[Int]
+ case IsZero(n: Tree[Int]) extends Tree[Boolean]
+ case If(cond: Tree[Boolean], thenp: Tree[T], elsep: Tree[T])
+}
+
+object Test {
+ import Tree._
+
+ def eval[T](e: Tree[T]): T = e match {
+ case True => true
+ case False => false
+ case Zero => 0
+ case Succ(f) => eval(f) + 1
+ case Pred(f) => eval(f) - 1
+ case IsZero(f) => eval(f) == 0
+ case If(cond, thenp, elsep) => if (eval(cond)) eval(thenp) else eval(elsep)
+ }
+
+ val data = If(IsZero(Pred(Succ(Zero))), Succ(Succ(Zero)), Pred(Pred(Zero)))
+
+ def main(args: Array[String]) = {
+ println(s"$data --> ${eval(data)}")
+ }
+}
diff --git a/tests/run/enum-approx.scala b/tests/run/enum-approx.scala
new file mode 100644
index 000000000..7811b3909
--- /dev/null
+++ b/tests/run/enum-approx.scala
@@ -0,0 +1,22 @@
+enum class Fun[-T, +U >: Null] {
+ def f: T => U = null
+}
+object Fun {
+ case Identity[T, U >: Null](override val f: T => U) extends Fun[T, U]
+ case ConstNull {
+ override def f = x => null
+ }
+ case ConstNullClass() {
+ override def f = x => null
+ }
+ case ConstNullSimple
+}
+
+object Test {
+ def main(args: Array[String]) = {
+ val x: Null = Fun.ConstNull.f("abc")
+ val y: Null = Fun.ConstNullClass().f("abc")
+ assert(Fun.ConstNullSimple.f == null)
+ }
+}
+
diff --git a/tests/run/enumList2a.check b/tests/run/enumList2a.check
new file mode 100644
index 000000000..1d4812de1
--- /dev/null
+++ b/tests/run/enumList2a.check
@@ -0,0 +1 @@
+Cons(1,Cons(2,Cons(3,Nil)))
diff --git a/tests/run/generic/Color.scala b/tests/run/generic/Color.scala
index ed248295d..7f2a8818c 100644
--- a/tests/run/generic/Color.scala
+++ b/tests/run/generic/Color.scala
@@ -10,12 +10,17 @@ import Shapes._
*/
sealed trait Color extends Enum
-object Color extends EnumValues[Color](3) {
+object Color {
+
+ private val $values = new runtime.EnumValues[Color]
+ def valueOf = $values.fromInt
+ def withName = $values.fromName
+ def values = $values.values
private def $new(tag: Int, name: String) = new Color {
def enumTag = tag
override def toString = name
- registerEnumValue(this)
+ $values.register(this)
}
val Red: Color = $new(0, "Red")
@@ -25,6 +30,6 @@ object Color extends EnumValues[Color](3) {
implicit val ColorShape: Color `shaped` EnumValue[Color] =
new (Color `shaped` EnumValue[Color]) {
def toShape(x: Color) = EnumValue(x.enumTag)
- def fromShape(x: EnumValue[Color]) = Color.value(x.tag)
+ def fromShape(x: EnumValue[Color]) = Color.valueOf(x.tag)
}
-} \ No newline at end of file
+}
diff --git a/tests/run/generic/Enum.scala b/tests/run/generic/Enum.scala
deleted file mode 100644
index dbdbfe8eb..000000000
--- a/tests/run/generic/Enum.scala
+++ /dev/null
@@ -1,18 +0,0 @@
-package generic
-
-import Shapes.Singleton
-
-trait Enum {
- def enumTag: Int
-}
-
-trait FiniteEnum extends Enum
-
-abstract class EnumValues[E <: Enum](numVals: Int) {
- private var myValues = new Array[AnyRef](numVals)
-
- def registerEnumValue(v: E) =
- myValues(v.enumTag) = v
-
- def value: IndexedSeq[E] = (myValues: IndexedSeq[AnyRef]).asInstanceOf[IndexedSeq[E]]
-}
diff --git a/tests/run/generic/List.scala b/tests/run/generic/List.scala
index 3f3657656..bc01ce63f 100644
--- a/tests/run/generic/List.scala
+++ b/tests/run/generic/List.scala
@@ -46,7 +46,7 @@ object List0 {
}
}
-/** enum List[T] {
+/** enum List[+T] {
* case Cons(x: T, xs: List[T])
* case Nil extends List[Nothing]
* }
diff --git a/tests/run/generic/SearchResult.scala b/tests/run/generic/SearchResult.scala
index 1c86d1b4f..d4380a072 100644
--- a/tests/run/generic/SearchResult.scala
+++ b/tests/run/generic/SearchResult.scala
@@ -11,12 +11,17 @@ import Shapes._
*/
sealed trait SearchResult extends Enum
-object SearchResult extends EnumValues[SearchResult](3) {
+object SearchResult extends {
+
+ private val $values = new runtime.EnumValues[SearchResult]
+ def valueOf = $values.fromInt
+ def withName = $values.fromName
+ def values = $values.values
private def $new(tag: Int, name: String) = new SearchResult {
def enumTag = tag
override def toString = name
- registerEnumValue(this)
+ $values.register(this)
}
abstract case class Success(result: Color) extends SearchResult {
@@ -31,8 +36,8 @@ object SearchResult extends EnumValues[SearchResult](3) {
}
}
- val Diverging = $new(1, "Diverging")
- val NoMatch = $new(2, "NoMatch")
+ val Diverging: SearchResult = $new(1, "Diverging")
+ val NoMatch: SearchResult = $new(2, "NoMatch")
abstract case class Ambiguous(alt1: SearchResult, alt2: SearchResult) extends SearchResult {
def enumTag = 3
@@ -58,7 +63,7 @@ object SearchResult extends EnumValues[SearchResult](3) {
def fromShape(x: Sum[Success, Sum[Ambiguous, EnumValue[SearchResult]]]): SearchResult = x match {
case Fst(s) => s
case Snd(Fst(a)) => a
- case Snd(Snd(ev)) => value(ev.tag)
+ case Snd(Snd(ev)) => valueOf(ev.tag)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/run/generic/Tree.scala b/tests/run/generic/Tree.scala
index f4e706944..673506b07 100644
--- a/tests/run/generic/Tree.scala
+++ b/tests/run/generic/Tree.scala
@@ -2,14 +2,14 @@ package generic
import Shapes._
-/** enum Tree[TS] {
+/** enum Tree[T] {
* case True extends Tree[Boolean]
* case False extends Tree[Boolean]
* case Zero extends Tree[Int]
* case Succ(n: Tree[Int]) extends Tree[Int]
* case Pred(n: Tree[Int]) extends Tree[Int]
* case IsZero(n: Tree[Int]) extends Tree[Boolean]
- * case If(cond: Boolean, thenp: Tree[T], elsep: Tree[T]) extends Tree[T]
+ * case If(cond: Boolean, thenp: Tree[T], elsep: Tree[T])
* }
*/
sealed trait Tree[TR] extends Enum
@@ -110,4 +110,4 @@ object Tree {
case Snd(Snd(_if)) => _if
}
}
-} \ No newline at end of file
+}
diff --git a/tests/run/planets.check b/tests/run/planets.check
new file mode 100644
index 000000000..feb6f737d
--- /dev/null
+++ b/tests/run/planets.check
@@ -0,0 +1,8 @@
+Your weight on MERCURY is 37.775761520093525
+Your weight on SATURN is 106.60155388115666
+Your weight on VENUS is 90.49990998410455
+Your weight on URANUS is 90.51271993894251
+Your weight on EARTH is 100.0
+Your weight on NEPTUNE is 113.83280724696579
+Your weight on MARS is 37.873718403712886
+Your weight on JUPITER is 253.05575254957407
diff --git a/tests/run/planets.scala b/tests/run/planets.scala
new file mode 100644
index 000000000..bcbfd7eeb
--- /dev/null
+++ b/tests/run/planets.scala
@@ -0,0 +1,26 @@
+enum class Planet(mass: Double, radius: Double) {
+ private final val G = 6.67300E-11
+ def surfaceGravity = G * mass / (radius * radius)
+ def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity
+}
+object Planet {
+ case MERCURY extends Planet(3.303e+23, 2.4397e6)
+ case VENUS extends Planet(4.869e+24, 6.0518e6)
+ case EARTH extends Planet(5.976e+24, 6.37814e6)
+ case MARS extends Planet(6.421e+23, 3.3972e6)
+ case JUPITER extends Planet(1.9e+27, 7.1492e7)
+ case SATURN extends Planet(5.688e+26, 6.0268e7)
+ case URANUS extends Planet(8.686e+25, 2.5559e7)
+ case NEPTUNE extends Planet(1.024e+26, 2.4746e7)
+}
+object Test {
+ def main(args: Array[String]) = {
+ import Planet._
+ assert(enumValueNamed("SATURN") == SATURN)
+ assert(enumValue(2) == EARTH)
+ val earthWeight = 100
+ val mass = earthWeight/EARTH.surfaceGravity
+ for (p <- enumValues)
+ println(s"Your weight on $p is ${p.surfaceWeight(mass)}")
+ }
+}