diff options
author | odersky <odersky@gmail.com> | 2017-04-06 13:12:05 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-06 13:12:05 +0200 |
commit | 62c2a1e2d6265cf7f096e4c4e51e4e883bce1514 (patch) | |
tree | 5cd2f9018b4a5ed5fcd1ebe6a6aed392d3fd00b4 | |
parent | 2556c83a04af1baf9dd69f6139e9ea61d39e7c6a (diff) | |
parent | 30d8d878118c537ff82c88ef7ade8780b390bfae (diff) | |
download | dotty-62c2a1e2d6265cf7f096e4c4e51e4e883bce1514.tar.gz dotty-62c2a1e2d6265cf7f096e4c4e51e4e883bce1514.tar.bz2 dotty-62c2a1e2d6265cf7f096e4c4e51e4e883bce1514.zip |
Merge pull request #1958 from dotty-staging/add-enum
Add "enum" construct
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)}") + } +} |