Skip to content

Commit a46f4f8

Browse files
committed
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.
1 parent 1c79612 commit a46f4f8

File tree

2 files changed

+57
-12
lines changed

2 files changed

+57
-12
lines changed

compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
88
import Decorators._
99
import collection.mutable.ListBuffer
1010
import util.Property
11-
import reporting.diagnostic.messages._
11+
import typer.ErrorReporting._
1212

1313
object DesugarEnums {
1414
import untpd._
@@ -56,6 +56,31 @@ object DesugarEnums {
5656
}
5757
}
5858

59+
/** A reference to the enum class `E`, possibly followed by type arguments.
60+
* Each covariant type parameter is approximated by its lower bound.
61+
* Each contravariant type parameter is approximated by its upper bound.
62+
* It is an error if a type parameter is non-variant, or if its approximation
63+
* refers to pther type parameters.
64+
*/
65+
def interpolatedEnumParent(pos: Position)(implicit ctx: Context): Tree = {
66+
val tparams = enumClass.typeParams
67+
def isGround(tp: Type) = tp.subst(tparams, tparams.map(_ => NoType)) eq tp
68+
val targs = tparams map { tparam =>
69+
if (tparam.variance > 0 && isGround(tparam.info.bounds.lo))
70+
tparam.info.bounds.lo
71+
else if (tparam.variance < 0 && isGround(tparam.info.bounds.hi))
72+
tparam.info.bounds.hi
73+
else {
74+
def problem =
75+
if (tparam.variance == 0) "is non variant"
76+
else "has bounds that depend on a type parameter in the same parameter list"
77+
errorType(i"""cannot determine type argument for enum parent $enumClass,
78+
|type parameter $tparam $problem""", pos)
79+
}
80+
}
81+
TypeTree(enumClass.typeRef.appliedTo(targs)).withPos(pos)
82+
}
83+
5984
def enumTagMeth(implicit ctx: Context) =
6085
DefDef(nme.enumTag, Nil, Nil, TypeTree(),
6186
Literal(Constant(nextEnumTag(isSimpleCase = false)._1)))
@@ -111,7 +136,12 @@ object DesugarEnums {
111136

112137
def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree =
113138
if (impl.parents.isEmpty)
114-
expandSimpleEnumCase(name, mods, pos)
139+
if (impl.body.isEmpty)
140+
expandSimpleEnumCase(name, mods, pos)
141+
else {
142+
val parent = interpolatedEnumParent(pos)
143+
expandEnumModule(name, cpy.Template(impl)(parents = parent :: Nil), mods, pos)
144+
}
115145
else {
116146
def toStringMeth =
117147
DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString)))
@@ -121,13 +151,17 @@ object DesugarEnums {
121151
ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final).withPos(pos)
122152
}
123153

124-
def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = {
125-
if (reconstitutedEnumTypeParams(pos).nonEmpty)
126-
ctx.error(i"illegal enum value of generic $enumClass: an explicit `extends' clause is needed", pos)
127-
val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true)
128-
val prefix = if (simpleSeen) Nil else enumScaffolding
129-
val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString))))
130-
val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final).withPos(pos)
131-
flatTree(prefix ::: vdef :: Nil).withPos(pos.startPos)
132-
}
154+
def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree =
155+
if (reconstitutedEnumTypeParams(pos).nonEmpty) {
156+
val parent = interpolatedEnumParent(pos)
157+
val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, Nil)
158+
expandEnumModule(name, impl, mods, pos)
159+
}
160+
else {
161+
val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true)
162+
val prefix = if (simpleSeen) Nil else enumScaffolding
163+
val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString))))
164+
val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final).withPos(pos)
165+
flatTree(prefix ::: vdef :: Nil).withPos(pos.startPos)
166+
}
133167
}

tests/neg/enums.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
enum List[+T] {
22
case Cons(x: T, xs: List[T])
3-
case Nil // error: illegal enum value
43
case Snoc[U](xs: List[U], x: U) // error: case with type parameters needs extends clause
54
}
65

76
enum class X {
87
case Y // error: case not allowed here
98
}
9+
10+
enum E1[T] {
11+
case C
12+
}
13+
14+
enum E2[+T, +U >: T] {
15+
case C
16+
}
17+
18+
enum E3[-T <: Ordered[T]] {
19+
case C
20+
}

0 commit comments

Comments
 (0)