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 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/neg/enums.scala (limited to 'tests/neg') 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 +} -- 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/neg') 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 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/neg') 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/neg') 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/neg') 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