Skip to content

Commit b6df668

Browse files
committed
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.
1 parent 6d6e88c commit b6df668

File tree

3 files changed

+46
-16
lines changed

3 files changed

+46
-16
lines changed

Diff for: compiler/src/dotty/tools/dotc/ast/Desugar.scala

+12-8
Original file line numberDiff line numberDiff line change
@@ -296,11 +296,16 @@ object desugar {
296296
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
297297
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.
298298

299+
lazy val reconstitutedTypeParams = reconstitutedEnumTypeParams(cdef.pos.startPos)
300+
299301
val originalTparams =
300302
if (isEnumCase && parents.isEmpty) {
301-
if (constr1.tparams.nonEmpty)
302-
ctx.error(em"case with type parameters needs extends clause", constr1.tparams.head.pos)
303-
reconstitutedEnumTypeParams(cdef.pos.startPos)
303+
if (constr1.tparams.nonEmpty) {
304+
if (reconstitutedTypeParams.nonEmpty)
305+
ctx.error(em"case with type parameters needs extends clause", constr1.tparams.head.pos)
306+
constr1.tparams
307+
}
308+
else reconstitutedTypeParams
304309
}
305310
else constr1.tparams
306311
val originalVparamss = constr1.vparamss
@@ -339,7 +344,9 @@ object desugar {
339344
// a reference to the class type bound by `cdef`, with type parameters coming from the constructor
340345
val classTypeRef = appliedRef(classTycon)
341346
// a reference to `enumClass`, with type parameters coming from the constructor
342-
lazy val enumClassTypeRef = appliedRef(enumClassRef)
347+
lazy val enumClassTypeRef =
348+
if (reconstitutedTypeParams.isEmpty) enumClassRef
349+
else appliedRef(enumClassRef)
343350

344351
// new C[Ts](paramss)
345352
lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef)
@@ -516,10 +523,7 @@ object desugar {
516523
case _ =>
517524
}
518525

519-
val result = flatTree(cdef1 :: companions ::: implicitWrappers)
520-
//if (isEnum) println(i"enum $cdef\n --->\n$result")
521-
//if (isEnumCase) println(i"enum case $cdef\n --->\n$result")
522-
result
526+
flatTree(cdef1 :: companions ::: implicitWrappers)
523527
}
524528

525529
val AccessOrSynthetic = AccessFlags | Synthetic

Diff for: compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala

+12-8
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ object DesugarEnums {
1414
import untpd._
1515
import desugar.DerivedFromParamTree
1616

17-
val EnumCaseCount = new Property.Key[Int]
17+
/** Attachment containing: The number of enum cases seen so far, and whether a
18+
* simple enum case was already seen.
19+
*/
20+
val EnumCaseCount = new Property.Key[(Int, Boolean)]
1821

1922
def enumClass(implicit ctx: Context) = ctx.owner.linkedClass
2023

21-
def nextEnumTag(implicit ctx: Context): Int = {
22-
val result = ctx.tree.removeAttachment(EnumCaseCount).getOrElse(0)
23-
ctx.tree.pushAttachment(EnumCaseCount, result + 1)
24-
result
24+
def nextEnumTag(isSimpleCase: Boolean)(implicit ctx: Context): (Int, Boolean) = {
25+
val (count, simpleSeen) = ctx.tree.removeAttachment(EnumCaseCount).getOrElse((0, false))
26+
ctx.tree.pushAttachment(EnumCaseCount, (count + 1, simpleSeen | isSimpleCase))
27+
(count, simpleSeen)
2528
}
2629

2730
def isLegalEnumCase(tree: MemberDef)(implicit ctx: Context): Boolean =
@@ -54,7 +57,8 @@ object DesugarEnums {
5457
}
5558

5659
def enumTagMeth(implicit ctx: Context) =
57-
DefDef(nme.enumTag, Nil, Nil, TypeTree(), Literal(Constant(nextEnumTag)))
60+
DefDef(nme.enumTag, Nil, Nil, TypeTree(),
61+
Literal(Constant(nextEnumTag(isSimpleCase = false)._1)))
5862

5963
def enumClassRef(implicit ctx: Context) = TypeTree(enumClass.typeRef)
6064

@@ -120,8 +124,8 @@ object DesugarEnums {
120124
def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = {
121125
if (reconstitutedEnumTypeParams(pos).nonEmpty)
122126
ctx.error(i"illegal enum value of generic $enumClass: an explicit `extends' clause is needed", pos)
123-
val tag = nextEnumTag
124-
val prefix = if (tag == 0) enumScaffolding else Nil
127+
val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true)
128+
val prefix = if (simpleSeen) Nil else enumScaffolding
125129
val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString))))
126130
val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final).withPos(pos)
127131
flatTree(prefix ::: vdef :: Nil).withPos(pos.startPos)

Diff for: tests/run/enum-HList.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
enum HLst {
2+
case HCons[+Hd, +Tl <: HLst](hd: Hd, tl: Tl)
3+
case HNil
4+
}
5+
6+
object Test {
7+
import HLst._
8+
def length(hl: HLst): Int = hl match {
9+
case HCons(_, tl) => 1 + length(tl)
10+
case HNil => 0
11+
}
12+
def sumInts(hl: HLst): Int = hl match {
13+
case HCons(x: Int, tl) => x + sumInts(tl)
14+
case HCons(_, tl) => sumInts(tl)
15+
case HNil => 0
16+
}
17+
def main(args: Array[String]) = {
18+
val hl = HCons(1, HCons("A", HNil))
19+
assert(length(hl) == 2, length(hl))
20+
assert(sumInts(hl) == 1)
21+
}
22+
}

0 commit comments

Comments
 (0)