From 69fd9dc80e78feb35ee601a9aaac813eef331e6a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 8 Feb 2017 21:17:36 +1100 Subject: Adapt generic tests to model modified enum values scheme --- tests/run/generic/Color.scala | 12 ++++++++---- tests/run/generic/Enum.scala | 20 +++++++++++++------- tests/run/generic/List.scala | 2 +- tests/run/generic/SearchResult.scala | 16 ++++++++++------ tests/run/generic/Tree.scala | 6 +++--- 5 files changed, 35 insertions(+), 21 deletions(-) (limited to 'tests') diff --git a/tests/run/generic/Color.scala b/tests/run/generic/Color.scala index ed248295d..0a0f3f1e5 100644 --- a/tests/run/generic/Color.scala +++ b/tests/run/generic/Color.scala @@ -10,12 +10,16 @@ import Shapes._ */ sealed trait Color extends Enum -object Color extends EnumValues[Color](3) { +object Color { + + private val $values = new EnumValues[Color] + def valueOf: Int => Color = $values + 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 +29,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 index dbdbfe8eb..38c9022d5 100644 --- a/tests/run/generic/Enum.scala +++ b/tests/run/generic/Enum.scala @@ -1,6 +1,8 @@ package generic import Shapes.Singleton +import scala.collection.mutable.ResizableArray +import scala.collection.immutable.Seq trait Enum { def enumTag: Int @@ -8,11 +10,15 @@ trait Enum { 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]] +class EnumValues[E <: Enum] extends ResizableArray[E] { + private var valuesCache: Seq[E] = Nil + def register(v: E) = { + ensureSize(v.enumTag + 1) + array(v.enumTag) = v + valuesCache = null + } + def values: Seq[E] = { + if (valuesCache == null) valuesCache = array.filter(_ != null).toList.asInstanceOf[scala.List[E]] + valuesCache + } } 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..9a747fc48 100644 --- a/tests/run/generic/SearchResult.scala +++ b/tests/run/generic/SearchResult.scala @@ -11,12 +11,16 @@ import Shapes._ */ sealed trait SearchResult extends Enum -object SearchResult extends EnumValues[SearchResult](3) { +object SearchResult extends { + + private val $values = new EnumValues[SearchResult] + def valueOf: Int => SearchResult = $values + 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 +35,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 +62,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 +} -- cgit v1.2.3 From c2790577d64b039792618c92e6ab7cff7a7ed824 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 8 Feb 2017 21:23:47 +1100 Subject: Add tests --- tests/neg/enums.scala | 8 ++++++++ tests/run/enum-Color.check | 3 +++ tests/run/enum-Color.scala | 13 +++++++++++++ tests/run/enum-List1.check | 1 + tests/run/enum-List1.scala | 10 ++++++++++ tests/run/enum-List2.check | 1 + tests/run/enum-List2.scala | 11 +++++++++++ 7 files changed, 47 insertions(+) create mode 100644 tests/neg/enums.scala create mode 100644 tests/run/enum-Color.check create mode 100644 tests/run/enum-Color.scala create mode 100644 tests/run/enum-List1.check create mode 100644 tests/run/enum-List1.scala create mode 100644 tests/run/enum-List2.check create mode 100644 tests/run/enum-List2.scala (limited to 'tests') diff --git a/tests/neg/enums.scala b/tests/neg/enums.scala new file mode 100644 index 000000000..83311f37c --- /dev/null +++ b/tests/neg/enums.scala @@ -0,0 +1,8 @@ +enum List[+T] { + case Cons(x: T, xs: List[T]) + case Nil // error: illegal enum value +} + +enum class X { + case Y // error: case not allowed here +} 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..24916abee --- /dev/null +++ b/tests/run/enum-Color.scala @@ -0,0 +1,13 @@ +enum Color { + case Red + case Green + case Blue +} + +object Test { + def main(args: Array[String]) = + for (color <- Color.values) { + println(s"$color: ${color.enumTag}") + assert(Color.valueOf(color.enumTag) eq color) + } +} 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) +} + -- cgit v1.2.3 From 2be70e6019cff01d8a4bc40b90614c64d5d638ff Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 9 Feb 2017 22:49:19 +1100 Subject: Another test --- tests/run/enum-List3.check | 1 + tests/run/enum-List3.scala | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/run/enum-List3.check create mode 100644 tests/run/enum-List3.scala (limited to 'tests') 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) +} + -- cgit v1.2.3 From 7a7db73ae9738f6ed63772851fa5e9820628b015 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 9 Feb 2017 23:16:59 +1100 Subject: Link generic test to actual implementations of Enum and EnumValues These are now implemented in scala.runtime. --- tests/run/generic/Color.scala | 2 +- tests/run/generic/Enum.scala | 24 ------------------------ tests/run/generic/SearchResult.scala | 2 +- 3 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 tests/run/generic/Enum.scala (limited to 'tests') diff --git a/tests/run/generic/Color.scala b/tests/run/generic/Color.scala index 0a0f3f1e5..183f18349 100644 --- a/tests/run/generic/Color.scala +++ b/tests/run/generic/Color.scala @@ -12,7 +12,7 @@ sealed trait Color extends Enum object Color { - private val $values = new EnumValues[Color] + private val $values = new runtime.EnumValues[Color] def valueOf: Int => Color = $values def values = $values.values diff --git a/tests/run/generic/Enum.scala b/tests/run/generic/Enum.scala deleted file mode 100644 index 38c9022d5..000000000 --- a/tests/run/generic/Enum.scala +++ /dev/null @@ -1,24 +0,0 @@ -package generic - -import Shapes.Singleton -import scala.collection.mutable.ResizableArray -import scala.collection.immutable.Seq - -trait Enum { - def enumTag: Int -} - -trait FiniteEnum extends Enum - -class EnumValues[E <: Enum] extends ResizableArray[E] { - private var valuesCache: Seq[E] = Nil - def register(v: E) = { - ensureSize(v.enumTag + 1) - array(v.enumTag) = v - valuesCache = null - } - def values: Seq[E] = { - if (valuesCache == null) valuesCache = array.filter(_ != null).toList.asInstanceOf[scala.List[E]] - valuesCache - } -} diff --git a/tests/run/generic/SearchResult.scala b/tests/run/generic/SearchResult.scala index 9a747fc48..d39ee89a0 100644 --- a/tests/run/generic/SearchResult.scala +++ b/tests/run/generic/SearchResult.scala @@ -13,7 +13,7 @@ sealed trait SearchResult extends Enum object SearchResult extends { - private val $values = new EnumValues[SearchResult] + private val $values = new runtime.EnumValues[SearchResult] def valueOf: Int => SearchResult = $values def values = $values.values -- cgit v1.2.3 From d3b2c37b9fb4211ade0660c3985f9f14a0ffa505 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 10 Feb 2017 11:36:25 +1100 Subject: New test case --- tests/pos/enum-List-control.scala | 14 ++++++++++++++ tests/run/enum-Tree.check | 1 + tests/run/enum-Tree.scala | 29 +++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 tests/pos/enum-List-control.scala create mode 100644 tests/run/enum-Tree.check create mode 100644 tests/run/enum-Tree.scala (limited to 'tests') 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-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)}") + } +} -- cgit v1.2.3 From c245600ed4bfd4af6f2b45e8cae2cf5a63ddeaf0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 11 Feb 2017 00:11:25 +1100 Subject: More fine-grained distinctions when flags are defined. Flags like Trait are in fact not always defined when a symbol is created. For symbols loaded from class files, this flag, and some other is defined only once the classfile has been loaded. But this happens in general before the symbol is completed. We model this distinction by separating from the `FromStartFlags` set a new set `AfterLoadFlags` and distinguishing between the two sets in `SymDenotations#is`. Test case is enum-Option.scala. This erroneously complained before that `Enum` was not a trait. --- compiler/src/dotty/tools/dotc/core/Flags.scala | 17 +++++++++++++---- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 13 +++++++++---- .../tools/dotc/reporting/diagnostic/messages.scala | 2 +- tests/run/enum-Option.scala | 16 ++++++++++++++++ 4 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 tests/run/enum-Option.scala (limited to 'tests') 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/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/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/tests/run/enum-Option.scala b/tests/run/enum-Option.scala new file mode 100644 index 000000000..74e449daf --- /dev/null +++ b/tests/run/enum-Option.scala @@ -0,0 +1,16 @@ +enum class Option[+T] extends Serializable { + def isDefined: Boolean +} +object Option { + case Some(x: T) { + def isDefined = true + } + case None extends Option[Nothing] { + def isDefined = false + } +} + +object Test { + def main(args: Array[String]) = + assert(Some(None).isDefined) +} -- cgit v1.2.3 From a30e7ecabc84583fa93bc215b1d1b5186caec07b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 13 Feb 2017 13:09:11 +0100 Subject: Check that cases with type parameters also have an extends clause --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 6 +++++- tests/neg/enums.scala | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 3a3acb06c..cf128c26e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -297,7 +297,11 @@ object desugar { // This is not watertight, but `extends AnyVal` will be replaced by `inline` later. val originalTparams = - if (isEnumCase && parents.isEmpty) reconstitutedEnumTypeParams(cdef.pos.startPos) + if (isEnumCase && parents.isEmpty) { + if (constr1.tparams.nonEmpty) + ctx.error(em"case with type parameters needs extends clause", constr1.tparams.head.pos) + reconstitutedEnumTypeParams(cdef.pos.startPos) + } else constr1.tparams val originalVparamss = constr1.vparamss val constrTparams = originalTparams.map(toDefParam) diff --git a/tests/neg/enums.scala b/tests/neg/enums.scala index 83311f37c..2dc8999fa 100644 --- a/tests/neg/enums.scala +++ b/tests/neg/enums.scala @@ -1,6 +1,7 @@ enum List[+T] { case Cons(x: T, xs: List[T]) case Nil // error: illegal enum value + case Snoc[U](xs: List[U], x: U) // error: case with type parameters needs extends clause // error // error // error } enum class X { -- cgit v1.2.3 From 4bdad3c21a1461bed6e91ef69dd767fa5211f60d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 14 Feb 2017 09:45:44 +0100 Subject: Change return type of `apply`. In an enum case like case C() extends P1 with ... with Pn ... apply now returns `P1 & ... & Pn`, where before it was just P1. Also, add to Option test. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 14 +++++++------- tests/run/enum-Option.scala | 5 ++++- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index aa073429d..575af97f4 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -344,13 +344,6 @@ object desugar { // new C[Ts](paramss) lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef) - // The return type of the `apply` and `copy` methods - val applyResultTpt = - if (isEnumCase) - if (parents.isEmpty) enumClassTypeRef - else parents.head - else TypeTree() - // Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams) // def isDefined = true // def productArity = N @@ -436,6 +429,13 @@ 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 || diff --git a/tests/run/enum-Option.scala b/tests/run/enum-Option.scala index 74e449daf..b9efadf0d 100644 --- a/tests/run/enum-Option.scala +++ b/tests/run/enum-Option.scala @@ -2,6 +2,7 @@ enum class Option[+T] extends Serializable { def isDefined: Boolean } object Option { + def apply[T](x: T): Option[T] = if (x == null) None else Some(x) case Some(x: T) { def isDefined = true } @@ -11,6 +12,8 @@ object Option { } object Test { - def main(args: Array[String]) = + def main(args: Array[String]) = { assert(Some(None).isDefined) + Option(22) match { case Option.Some(x) => assert(x == 22) } + } } -- cgit v1.2.3 From cf10e28a54aaeab124f9a939f71da7e09d299bcb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 14 Feb 2017 11:17:53 +0100 Subject: Change enumeration members. Based on the discussion in #1970, enumeration objects now have three public members: - valueOf: Map[Int, E] - withName: Map[String, E] - values: Iterable[E] Also, the variance of case type parameters is now the same as in the corresponding type parameter of the enum class. --- .../src/dotty/tools/dotc/ast/DesugarEnums.scala | 26 ++++++++++++---------- compiler/src/dotty/tools/dotc/core/StdNames.scala | 1 - library/src/scala/runtime/EnumValues.scala | 25 ++++++++++++--------- tests/run/generic/Color.scala | 3 ++- tests/run/generic/SearchResult.scala | 3 ++- 5 files changed, 32 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 4317c8183..8bd3c8580 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -34,7 +34,8 @@ object DesugarEnums { } /** Type parameters reconstituted from the constructor - * of the `enum' class corresponding to an enum case + * 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 { @@ -43,11 +44,11 @@ object DesugarEnums { case _ => Nil } - for (tparam <- tparams) yield { + (tparams, enumClass.typeParams).zipped.map { (tparam, ecTparam) => val tbounds = new DerivedFromParamTree tbounds.pushAttachment(OriginalSymbol, tparam) TypeDef(tparam.name, tbounds) - .withFlags(Param | PrivateLocal).withPos(pos) + .withFlags(Param | PrivateLocal | ecTparam.flags & VarianceFlags).withPos(pos) } } @@ -64,7 +65,8 @@ object DesugarEnums { /** The following lists of definitions for an enum type E: * * private val $values = new EnumValues[E] - * def valueOf: Int => E = $values + * def valueOf = $values.fromInt + * def withName = $values.fromName * def values = $values.values * * private def $new(tag: Int, name: String) = new E { @@ -74,32 +76,32 @@ object DesugarEnums { * } */ private def enumScaffolding(implicit ctx: Context): List[Tree] = { - val valsRef = Ident(nme.DOLLAR_VALUES) + def valuesDot(name: String) = Select(Ident(nme.DOLLAR_VALUES), name.toTermName) + def enumDefDef(name: String, select: String) = + DefDef(name.toTermName, Nil, Nil, TypeTree(), valuesDot(select)) def param(name: TermName, typ: Type) = ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param) val privateValuesDef = ValDef(nme.DOLLAR_VALUES, TypeTree(), New(TypeTree(defn.EnumValuesType.appliedTo(enumClass.typeRef :: Nil)), ListOfNil)) .withFlags(Private) - val valueOfDef = - DefDef(nme.valueOf, Nil, Nil, - TypeTree(defn.FunctionOf(defn.IntType :: Nil, enumClass.typeRef)), valsRef) - val valuesDef = - DefDef(nme.values, Nil, Nil, TypeTree(), Select(valsRef, nme.values)) + val valueOfDef = enumDefDef("valueOf", "fromInt") + val withNameDef = enumDefDef("withName", "fromName") + val valuesDef = enumDefDef("values", "values") val enumTagDef = DefDef(nme.enumTag, Nil, Nil, TypeTree(), Ident(nme.tag)) val toStringDef = DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name)) .withFlags(Override) val registerStat = - Apply(Select(valsRef, nme.register), This(EmptyTypeIdent) :: Nil) + Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil) def creator = New(Template(emptyConstructor, enumClassRef :: Nil, EmptyValDef, List(enumTagDef, toStringDef, registerStat))) val newDef = DefDef(nme.DOLLAR_NEW, Nil, List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), TypeTree(), creator) - List(privateValuesDef, valueOfDef, valuesDef, newDef) + List(privateValuesDef, valueOfDef, withNameDef, valuesDef, newDef) } def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index ff3ddbad7..bc3f96d91 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -477,7 +477,6 @@ object StdNames { val productPrefix: N = "productPrefix" val readResolve: N = "readResolve" val reflect : N = "reflect" - val register: N = "register" val reify : N = "reify" val rootMirror : N = "rootMirror" val runOrElse: N = "runOrElse" diff --git a/library/src/scala/runtime/EnumValues.scala b/library/src/scala/runtime/EnumValues.scala index 6d2e56cf3..6f9d907b3 100644 --- a/library/src/scala/runtime/EnumValues.scala +++ b/library/src/scala/runtime/EnumValues.scala @@ -1,18 +1,21 @@ package scala.runtime -import scala.collection.immutable.Seq -import scala.collection.mutable.ResizableArray +import scala.collection.immutable.Map + +class EnumValues[E <: Enum] { + private var myMap: Map[Int, E] = Map() + private var fromNameCache: Map[String, E] = null -class EnumValues[E <: Enum] extends ResizableArray[E] { - private var valuesCache: List[E] = Nil def register(v: E) = { - ensureSize(v.enumTag + 1) - size0 = size0 max (v.enumTag + 1) - array(v.enumTag) = v - valuesCache = null + require(!myMap.contains(v.enumTag)) + myMap = myMap.updated(v.enumTag, v) + fromNameCache = null } - def values: Seq[E] = { - if (valuesCache == null) valuesCache = array.filter(_ != null).toList.asInstanceOf[scala.List[E]] - valuesCache + + 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/run/generic/Color.scala b/tests/run/generic/Color.scala index 183f18349..7f2a8818c 100644 --- a/tests/run/generic/Color.scala +++ b/tests/run/generic/Color.scala @@ -13,7 +13,8 @@ sealed trait Color extends Enum object Color { private val $values = new runtime.EnumValues[Color] - def valueOf: Int => Color = $values + def valueOf = $values.fromInt + def withName = $values.fromName def values = $values.values private def $new(tag: Int, name: String) = new Color { diff --git a/tests/run/generic/SearchResult.scala b/tests/run/generic/SearchResult.scala index d39ee89a0..d4380a072 100644 --- a/tests/run/generic/SearchResult.scala +++ b/tests/run/generic/SearchResult.scala @@ -14,7 +14,8 @@ sealed trait SearchResult extends Enum object SearchResult extends { private val $values = new runtime.EnumValues[SearchResult] - def valueOf: Int => SearchResult = $values + def valueOf = $values.fromInt + def withName = $values.fromName def values = $values.values private def $new(tag: Int, name: String) = new SearchResult { -- cgit v1.2.3 From 91a26b3f42d1218015acb9b7e1bfc180e3ed779b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 27 Feb 2017 17:44:02 +0100 Subject: Support comma-separated enum constants --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 18 +++++++++-- .../src/dotty/tools/dotc/ast/DesugarEnums.scala | 35 ++++++++++++---------- .../src/dotty/tools/dotc/parsing/Parsers.scala | 19 +++++++----- docs/docs/internals/syntax.md | 2 +- tests/run/enum-Color.scala | 4 +-- 5 files changed, 48 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 575af97f4..04834b04b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -576,11 +576,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, diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 8bd3c8580..7fdff0e2d 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -24,14 +24,15 @@ object DesugarEnums { result } - def isLegalEnumCase(tree: MemberDef)(implicit ctx: Context): Boolean = { - tree.mods.hasMod[Mod.EnumCase] && - ( ctx.owner.is(ModuleClass) && enumClass.derivesFrom(defn.EnumClass) + def isLegalEnumCase(tree: MemberDef)(implicit ctx: Context): Boolean = + tree.mods.hasMod[Mod.EnumCase] && enumCaseIsLegal(tree) + + 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. @@ -104,23 +105,25 @@ object DesugarEnums { List(privateValuesDef, valueOfDef, withNameDef, valuesDef, newDef) } - def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = { - def nameLit = Literal(Constant(name.toString)) - if (impl.parents.isEmpty) { - if (reconstitutedEnumTypeParams(pos).nonEmpty) - ctx.error(i"illegal enum value of generic $enumClass: an explicit `extends' clause is needed", pos) - val tag = nextEnumTag - val prefix = if (tag == 0) enumScaffolding else Nil - val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), nameLit)) - val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final).withPos(pos) - flatTree(prefix ::: vdef :: Nil).withPos(pos.startPos) - } else { + def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = + if (impl.parents.isEmpty) + expandSimpleEnumCase(name, mods, pos) + else { def toStringMeth = - DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), nameLit) + DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString))) .withFlags(Override) val impl1 = cpy.Template(impl)(body = impl.body ++ List(enumTagMeth, toStringMeth)) ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final).withPos(pos) } + + def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = { + if (reconstitutedEnumTypeParams(pos).nonEmpty) + ctx.error(i"illegal enum value of generic $enumClass: an explicit `extends' clause is needed", pos) + val tag = nextEnumTag + val prefix = if (tag == 0) enumScaffolding else Nil + 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(prefix ::: vdef :: Nil).withPos(pos.startPos) } } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ee736179a..9864281a5 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2149,8 +2149,8 @@ object Parsers { } /** EnumCaseStats = EnumCaseStat {semi EnumCaseStat */ - def enumCaseStats(): List[MemberDef] = { - val cases = new ListBuffer[MemberDef] += enumCaseStat() + def enumCaseStats(): List[DefTree] = { + val cases = new ListBuffer[DefTree] += enumCaseStat() while (in.token != RBRACE) { acceptStatSep() cases += enumCaseStat() @@ -2159,19 +2159,24 @@ object Parsers { } /** EnumCaseStat = {Annotation [nl]} {Modifier} EnumCase */ - def enumCaseStat(): MemberDef = + def enumCaseStat(): DefTree = enumCase(in.offset, defAnnotsMods(modifierTokens)) /** EnumCase = `case' (EnumClassDef | ObjectDef) */ - def enumCase(start: Offset, mods: Modifiers): MemberDef = { + def enumCase(start: Offset, mods: Modifiers): DefTree = { val mods1 = mods.withAddedMod(atPos(in.offset)(Mod.EnumCase())) | Case accept(CASE) atPos(start, nameStart) { - val name = ident() + val id = termIdent() if (in.token == LBRACKET || in.token == LPAREN) - classDefRest(start, mods1, name.toTypeName) + 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, name) + objectDefRest(start, mods1, id.name.asTermName) } } diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index c7f282dc1..e4285e20f 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -341,7 +341,7 @@ ObjectDef ::= id TemplateOpt EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumDef(mods, name, tparams, template) [nl] ‘{’ EnumCaseStat {semi EnumCaseStat} ‘}’ EnumCaseStat ::= {Annotation [nl]} {Modifier} EnumCase -EnumCase ::= `case' (EnumClassDef | ObjectDef) +EnumCase ::= `case' (EnumClassDef | ObjectDef | ids) EnumClassDef ::= id [ClsTpeParamClause | ClsParamClause] ClassDef(mods, name, tparams, templ) ClsParamClauses TemplateOpt TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody] diff --git a/tests/run/enum-Color.scala b/tests/run/enum-Color.scala index 24916abee..1a077bf8e 100644 --- a/tests/run/enum-Color.scala +++ b/tests/run/enum-Color.scala @@ -1,7 +1,5 @@ enum Color { - case Red - case Green - case Blue + case Red, Green, Blue } object Test { -- cgit v1.2.3 From 44d9ab81886a9ff0b7256fccb584c049f1c18027 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 28 Feb 2017 18:59:08 +0100 Subject: Support cases with type parameters that extend a non-parameterized base Support cases with type parameters that implicitly extend a non-parameterized base without needing their own extends clause. The proposal has been updated to make clear that this is supported. Also address other reviewers comments. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 20 ++++++++++++-------- .../src/dotty/tools/dotc/ast/DesugarEnums.scala | 20 ++++++++++++-------- tests/run/enum-HList.scala | 22 ++++++++++++++++++++++ 3 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 tests/run/enum-HList.scala (limited to 'tests') diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 04834b04b..80da75678 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -296,11 +296,16 @@ object desugar { val isValueClass = parents.nonEmpty && isAnyVal(parents.head) // This is not watertight, but `extends AnyVal` will be replaced by `inline` later. + lazy val reconstitutedTypeParams = reconstitutedEnumTypeParams(cdef.pos.startPos) + val originalTparams = if (isEnumCase && parents.isEmpty) { - if (constr1.tparams.nonEmpty) - ctx.error(em"case with type parameters needs extends clause", constr1.tparams.head.pos) - reconstitutedEnumTypeParams(cdef.pos.startPos) + 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 @@ -339,7 +344,9 @@ object desugar { // 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 = appliedRef(enumClassRef) + lazy val enumClassTypeRef = + if (reconstitutedTypeParams.isEmpty) enumClassRef + else appliedRef(enumClassRef) // new C[Ts](paramss) lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef) @@ -516,10 +523,7 @@ object desugar { case _ => } - val result = flatTree(cdef1 :: companions ::: implicitWrappers) - //if (isEnum) println(i"enum $cdef\n --->\n$result") - //if (isEnumCase) println(i"enum case $cdef\n --->\n$result") - result + flatTree(cdef1 :: companions ::: implicitWrappers) } val AccessOrSynthetic = AccessFlags | Synthetic diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 7fdff0e2d..c5c95d647 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -14,14 +14,17 @@ object DesugarEnums { import untpd._ import desugar.DerivedFromParamTree - val EnumCaseCount = new Property.Key[Int] + /** Attachment containing: The number of enum cases seen so far, and whether a + * simple enum case was already seen. + */ + val EnumCaseCount = new Property.Key[(Int, Boolean)] def enumClass(implicit ctx: Context) = ctx.owner.linkedClass - def nextEnumTag(implicit ctx: Context): Int = { - val result = ctx.tree.removeAttachment(EnumCaseCount).getOrElse(0) - ctx.tree.pushAttachment(EnumCaseCount, result + 1) - result + def nextEnumTag(isSimpleCase: Boolean)(implicit ctx: Context): (Int, Boolean) = { + val (count, simpleSeen) = ctx.tree.removeAttachment(EnumCaseCount).getOrElse((0, false)) + ctx.tree.pushAttachment(EnumCaseCount, (count + 1, simpleSeen | isSimpleCase)) + (count, simpleSeen) } def isLegalEnumCase(tree: MemberDef)(implicit ctx: Context): Boolean = @@ -54,7 +57,8 @@ object DesugarEnums { } def enumTagMeth(implicit ctx: Context) = - DefDef(nme.enumTag, Nil, Nil, TypeTree(), Literal(Constant(nextEnumTag))) + DefDef(nme.enumTag, Nil, Nil, TypeTree(), + Literal(Constant(nextEnumTag(isSimpleCase = false)._1))) def enumClassRef(implicit ctx: Context) = TypeTree(enumClass.typeRef) @@ -120,8 +124,8 @@ object DesugarEnums { def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = { if (reconstitutedEnumTypeParams(pos).nonEmpty) ctx.error(i"illegal enum value of generic $enumClass: an explicit `extends' clause is needed", pos) - val tag = nextEnumTag - val prefix = if (tag == 0) enumScaffolding else Nil + val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true) + val prefix = if (simpleSeen) Nil else enumScaffolding 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(prefix ::: vdef :: Nil).withPos(pos.startPos) 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) + } +} -- cgit v1.2.3 From 62b4eb87aa72c3b4e40a9c847ec61dabe467dabd Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 2 Mar 2017 11:41:22 +0100 Subject: Fix neg test error count Previous expansion caused 3 spurious follow-on errors which are now avoided. --- tests/neg/enums.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/neg/enums.scala b/tests/neg/enums.scala index 2dc8999fa..d6f75e2b9 100644 --- a/tests/neg/enums.scala +++ b/tests/neg/enums.scala @@ -1,7 +1,7 @@ enum List[+T] { case Cons(x: T, xs: List[T]) case Nil // error: illegal enum value - case Snoc[U](xs: List[U], x: U) // error: case with type parameters needs extends clause // error // error // error + case Snoc[U](xs: List[U], x: U) // error: case with type parameters needs extends clause } enum class X { -- cgit v1.2.3 From a46f4f840a456bb70ef4b95e6b18608522075442 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Apr 2017 18:31:52 +0200 Subject: Infer enum type args from type parameter bounds Infer type arguments for enum paraments from corresponding type parameter bounds. This only works if the type parameter in question is variant and its bound is ground. --- .../src/dotty/tools/dotc/ast/DesugarEnums.scala | 56 +++++++++++++++++----- tests/neg/enums.scala | 13 ++++- 2 files changed, 57 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index c5c95d647..ae4ba23d5 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -8,7 +8,7 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ import Decorators._ import collection.mutable.ListBuffer import util.Property -import reporting.diagnostic.messages._ +import typer.ErrorReporting._ object DesugarEnums { import untpd._ @@ -56,6 +56,31 @@ object DesugarEnums { } } + /** 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) + } + def enumTagMeth(implicit ctx: Context) = DefDef(nme.enumTag, Nil, Nil, TypeTree(), Literal(Constant(nextEnumTag(isSimpleCase = false)._1))) @@ -111,7 +136,12 @@ object DesugarEnums { def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = if (impl.parents.isEmpty) - expandSimpleEnumCase(name, mods, pos) + 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))) @@ -121,13 +151,17 @@ object DesugarEnums { ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final).withPos(pos) } - def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = { - if (reconstitutedEnumTypeParams(pos).nonEmpty) - ctx.error(i"illegal enum value of generic $enumClass: an explicit `extends' clause is needed", pos) - val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true) - val prefix = if (simpleSeen) Nil else enumScaffolding - 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(prefix ::: vdef :: Nil).withPos(pos.startPos) - } + def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = + if (reconstitutedEnumTypeParams(pos).nonEmpty) { + val parent = interpolatedEnumParent(pos) + val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, Nil) + expandEnumModule(name, impl, mods, pos) + } + else { + val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true) + val prefix = if (simpleSeen) Nil else enumScaffolding + 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(prefix ::: vdef :: Nil).withPos(pos.startPos) + } } diff --git a/tests/neg/enums.scala b/tests/neg/enums.scala index d6f75e2b9..1ed3007e7 100644 --- a/tests/neg/enums.scala +++ b/tests/neg/enums.scala @@ -1,9 +1,20 @@ enum List[+T] { case Cons(x: T, xs: List[T]) - case Nil // error: illegal enum value 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 +} + +enum E2[+T, +U >: T] { + case C +} + +enum E3[-T <: Ordered[T]] { + case C +} -- cgit v1.2.3 From 9b37a7c2202c701691cb12b3d040645835d17ff7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Apr 2017 22:13:39 +0200 Subject: New and updated tests --- tests/neg/enums.scala | 6 +++--- tests/run/enum-List2a.scala | 11 +++++++++++ tests/run/enum-approx.scala | 22 ++++++++++++++++++++++ tests/run/enumList2a.check | 1 + 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/run/enum-List2a.scala create mode 100644 tests/run/enum-approx.scala create mode 100644 tests/run/enumList2a.check (limited to 'tests') diff --git a/tests/neg/enums.scala b/tests/neg/enums.scala index 1ed3007e7..108ec4a6c 100644 --- a/tests/neg/enums.scala +++ b/tests/neg/enums.scala @@ -8,13 +8,13 @@ enum class X { } enum E1[T] { - case C + case C // error: cannot determine type argument } enum E2[+T, +U >: T] { - case C + case C // error: cannot determine type argument } enum E3[-T <: Ordered[T]] { - case C + case C // error: cannot determine type argument } 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-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))) -- cgit v1.2.3 From 993834473c39b409bb2e21838a0e69fcc5fb4757 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Apr 2017 09:05:36 +0200 Subject: Add check file --- tests/run/enum-List2a.check | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/run/enum-List2a.check (limited to 'tests') 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))) -- cgit v1.2.3 From f7027732a1d1e92f4d7525f2a984a24fdb7a0053 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Apr 2017 17:09:15 +0200 Subject: Implementation of proposal changes - rename utility methods - generate utility methods also for object cases --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- .../src/dotty/tools/dotc/ast/DesugarEnums.scala | 119 +++++++++++++-------- tests/run/enum-Color.scala | 2 +- tests/run/enum-Option.scala | 8 +- tests/run/planets.scala | 26 +++++ 5 files changed, 107 insertions(+), 50 deletions(-) create mode 100644 tests/run/planets.scala (limited to 'tests') diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 80da75678..8499330fb 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -395,7 +395,7 @@ object desugar { .withMods(synthetic) :: Nil } - val enumTagMeths = if (isEnumCase) enumTagMeth :: Nil else Nil + val enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind.Class)._1 :: Nil else Nil copyMeths ::: enumTagMeths ::: productElemMeths.toList } else Nil diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index ae4ba23d5..e051824a1 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -10,26 +10,26 @@ import collection.mutable.ListBuffer import util.Property import typer.ErrorReporting._ +/** Helper methods to desugar enums */ object DesugarEnums { import untpd._ import desugar.DerivedFromParamTree - /** Attachment containing: The number of enum cases seen so far, and whether a - * simple enum case was already seen. - */ - val EnumCaseCount = new Property.Key[(Int, Boolean)] + @sharable object CaseKind extends Enumeration { + val Simple, Object, Class = Value + } - def enumClass(implicit ctx: Context) = ctx.owner.linkedClass + /** Attachment containing the number of enum cases and the smallest kind that was seen so far. */ + val EnumCaseCount = new Property.Key[(Int, CaseKind.Value)] - def nextEnumTag(isSimpleCase: Boolean)(implicit ctx: Context): (Int, Boolean) = { - val (count, simpleSeen) = ctx.tree.removeAttachment(EnumCaseCount).getOrElse((0, false)) - ctx.tree.pushAttachment(EnumCaseCount, (count + 1, simpleSeen | isSimpleCase)) - (count, simpleSeen) - } + /** 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) @@ -81,59 +81,89 @@ object DesugarEnums { TypeTree(enumClass.typeRef.appliedTo(targs)).withPos(pos) } - def enumTagMeth(implicit ctx: Context) = - DefDef(nme.enumTag, Nil, Nil, TypeTree(), - Literal(Constant(nextEnumTag(isSimpleCase = false)._1))) - + /** 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 = Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil) + /** The following lists of definitions for an enum type E: * * private val $values = new EnumValues[E] - * def valueOf = $values.fromInt - * def withName = $values.fromName - * def values = $values.values - * + * def enumValue = $values.fromInt + * def enumValueNamed = $values.fromName + * def enumValues = $values.values + */ + private def enumScaffolding(implicit ctx: Context): List[Tree] = { + val enumType = enumClass.typeRef.appliedTo(enumClass.typeParams.map(_ => TypeBounds.empty)) + 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(enumType :: 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 enumScaffolding(implicit ctx: Context): List[Tree] = { - def valuesDot(name: String) = Select(Ident(nme.DOLLAR_VALUES), name.toTermName) - def enumDefDef(name: String, select: String) = - DefDef(name.toTermName, Nil, Nil, TypeTree(), valuesDot(select)) + private def enumValueCreator(implicit ctx: Context) = { def param(name: TermName, typ: Type) = ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param) - val privateValuesDef = - ValDef(nme.DOLLAR_VALUES, TypeTree(), - New(TypeTree(defn.EnumValuesType.appliedTo(enumClass.typeRef :: Nil)), ListOfNil)) - .withFlags(Private) - val valueOfDef = enumDefDef("valueOf", "fromInt") - val withNameDef = enumDefDef("withName", "fromName") - val valuesDef = enumDefDef("values", "values") val enumTagDef = DefDef(nme.enumTag, Nil, Nil, TypeTree(), Ident(nme.tag)) val toStringDef = DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name)) .withFlags(Override) - val registerStat = - Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil) def creator = New(Template(emptyConstructor, enumClassRef :: Nil, EmptyValDef, - List(enumTagDef, toStringDef, registerStat))) - val newDef = - DefDef(nme.DOLLAR_NEW, Nil, - List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), - TypeTree(), creator) - List(privateValuesDef, valueOfDef, withNameDef, valuesDef, newDef) + 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 (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) @@ -146,22 +176,23 @@ object DesugarEnums { def toStringMeth = DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString))) .withFlags(Override) - val impl1 = cpy.Template(impl)(body = - impl.body ++ List(enumTagMeth, toStringMeth)) - ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final).withPos(pos) + 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 (reconstitutedEnumTypeParams(pos).nonEmpty) { + if (enumClass.typeParams.nonEmpty) { val parent = interpolatedEnumParent(pos) val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, Nil) expandEnumModule(name, impl, mods, pos) } else { - val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true) - val prefix = if (simpleSeen) Nil else enumScaffolding + 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(prefix ::: vdef :: Nil).withPos(pos.startPos) + flatTree(scaffolding ::: vdef :: Nil).withPos(pos.startPos) } } diff --git a/tests/run/enum-Color.scala b/tests/run/enum-Color.scala index 1a077bf8e..836e02c62 100644 --- a/tests/run/enum-Color.scala +++ b/tests/run/enum-Color.scala @@ -4,7 +4,7 @@ enum Color { object Test { def main(args: Array[String]) = - for (color <- Color.values) { + for (color <- Color.enumValues) { println(s"$color: ${color.enumTag}") assert(Color.valueOf(color.enumTag) eq color) } diff --git a/tests/run/enum-Option.scala b/tests/run/enum-Option.scala index b9efadf0d..76f5641b3 100644 --- a/tests/run/enum-Option.scala +++ b/tests/run/enum-Option.scala @@ -1,12 +1,12 @@ -enum class Option[+T] extends Serializable { +enum class Option[+T >: Null] extends Serializable { def isDefined: Boolean } object Option { - def apply[T](x: T): Option[T] = if (x == null) None else Some(x) + def apply[T >: Null](x: T): Option[T] = if (x == null) None else Some(x) case Some(x: T) { def isDefined = true } - case None extends Option[Nothing] { + case None { def isDefined = false } } @@ -14,6 +14,6 @@ object Option { object Test { def main(args: Array[String]) = { assert(Some(None).isDefined) - Option(22) match { case Option.Some(x) => assert(x == 22) } + Option("22") match { case Option.Some(x) => assert(x == "22") } } } diff --git a/tests/run/planets.scala b/tests/run/planets.scala new file mode 100644 index 000000000..2fff01edc --- /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 = args(0).toDouble + val mass = earthWeight/EARTH.surfaceGravity + for (p <- enumValues) + println(s"Your weight on $p is ${p.surfaceWeight(mass)}") + } +} -- cgit v1.2.3 From 92018967be69ffe660a225f0b58f9772388678fe Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Apr 2017 17:21:48 +0200 Subject: Update test and add check file --- tests/run/planets.check | 8 ++++++++ tests/run/planets.scala | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/run/planets.check (limited to 'tests') 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 index 2fff01edc..bcbfd7eeb 100644 --- a/tests/run/planets.scala +++ b/tests/run/planets.scala @@ -18,7 +18,7 @@ object Test { import Planet._ assert(enumValueNamed("SATURN") == SATURN) assert(enumValue(2) == EARTH) - val earthWeight = args(0).toDouble + val earthWeight = 100 val mass = earthWeight/EARTH.surfaceGravity for (p <- enumValues) println(s"Your weight on $p is ${p.surfaceWeight(mass)}") -- cgit v1.2.3 From 18d0ae5e08e66c90377ca6818b9d6f1af7f259ac Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Apr 2017 17:27:09 +0200 Subject: Another test fixed --- tests/run/enum-Color.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/run/enum-Color.scala b/tests/run/enum-Color.scala index 836e02c62..683d18d9e 100644 --- a/tests/run/enum-Color.scala +++ b/tests/run/enum-Color.scala @@ -6,6 +6,6 @@ object Test { def main(args: Array[String]) = for (color <- Color.enumValues) { println(s"$color: ${color.enumTag}") - assert(Color.valueOf(color.enumTag) eq color) + assert(Color.enumValue(color.enumTag) eq color) } } -- cgit v1.2.3