diff options
author | Martin Odersky <odersky@gmail.com> | 2017-04-04 18:31:52 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2017-04-04 18:31:52 +0200 |
commit | a46f4f840a456bb70ef4b95e6b18608522075442 (patch) | |
tree | 5a03fc0160085ccc87a8bdf75064af2f05eb023a | |
parent | 1c79612c57af81acec2480bef56240dcf5ec30d1 (diff) | |
download | dotty-a46f4f840a456bb70ef4b95e6b18608522075442.tar.gz dotty-a46f4f840a456bb70ef4b95e6b18608522075442.tar.bz2 dotty-a46f4f840a456bb70ef4b95e6b18608522075442.zip |
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.
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala | 56 | ||||
-rw-r--r-- | tests/neg/enums.scala | 13 |
2 files changed, 57 insertions, 12 deletions
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 +} |