From f50529f4f607acfda6205f109af4810737d33e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Bra=C4=8Devac?= Date: Sun, 5 Oct 2025 20:38:15 +0200 Subject: [PATCH] Fix cyclicity error for self-referential enum case Fixes #11443 When an enum case references itself in a parent type (e.g., `case Nn extends Opt[Nothing] with Comparable[Nn.type]`), the compiler previously reported a cyclic reference error during type inference. The fix attempts to provide an explicit type annotation (the intersection of all parents) for parameterless enum cases early at desugaring time. --- compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala | 10 +++++++++- tests/neg/i6601.scala | 10 +++++----- tests/pos/i11443.scala | 5 +++++ tests/{warn => pos}/i21860.scala | 2 +- 4 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 tests/pos/i11443.scala rename tests/{warn => pos}/i21860.scala (77%) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 7268ec720ce2..abe0abf05ede 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -289,7 +289,15 @@ object DesugarEnums { val (tag, scaffolding) = nextOrdinal(name, CaseKind.Object, definesLookups) val impl1 = cpy.Template(impl)(parents = impl.parents :+ scalaRuntimeDot(tpnme.EnumValue), body = Nil) .withAttachment(ExtendsSingletonMirror, ()) - val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods.withAddedFlags(EnumValue, span)) + + /** i11443: Attempt to provide an explicit type annotation for the enum case to allow certain cycles. + * We pick the intersection of all parents, but only if they can be determined to be all types at this point. + * Notably, this doesn't hold if one of the parents is a constructor call (e.g., extends Planet("Pluto")), + * which might involve yet-to-be inferred generic parameters. + */ + val tpt = if impl.parents.forall(_.isType) then impl.parents.reduceLeft(makeAndType(_, _)) else TypeTree() + + val vdef = ValDef(name, tpt, New(impl1)).withMods(mods.withAddedFlags(EnumValue, span)) flatTree(vdef :: scaffolding).withSpan(span) } } diff --git a/tests/neg/i6601.scala b/tests/neg/i6601.scala index 69c508ddc262..b6ab0a6c22d2 100644 --- a/tests/neg/i6601.scala +++ b/tests/neg/i6601.scala @@ -1,22 +1,22 @@ object GADTs2 { enum Var[G, A] { case Z[A, G]() extends Expr[(A, G), A] // error - case X extends AnyRef // error + case X extends AnyRef // error // error } enum Expr[G, A] { case Lit[G](n: Int) extends Expr[G, Int] // case S[A, G](x: } enum Covariant[+T] { - case Bottom extends AnyRef // error + case Bottom extends AnyRef // error // error } enum Contravariant[-T] { - case Top extends AnyRef // error + case Top extends AnyRef // error // error } enum Color { - case Red extends AnyRef // error + case Red extends AnyRef // error // error } enum Parameterized[T](x: T) { - case Foo extends AnyRef // error + case Foo extends AnyRef // error // error } } diff --git a/tests/pos/i11443.scala b/tests/pos/i11443.scala new file mode 100644 index 000000000000..b1a995ad80c8 --- /dev/null +++ b/tests/pos/i11443.scala @@ -0,0 +1,5 @@ +enum Opt[T] { + case Nn extends Opt[Nothing] with Comparable[Nn.type] + + def compareTo(nn: Nn.type) = 0 +} \ No newline at end of file diff --git a/tests/warn/i21860.scala b/tests/pos/i21860.scala similarity index 77% rename from tests/warn/i21860.scala rename to tests/pos/i21860.scala index 377d4761e80f..c3e16d9d443d 100644 --- a/tests/warn/i21860.scala +++ b/tests/pos/i21860.scala @@ -8,7 +8,7 @@ enum Shape extends Figure: case Ellipsis extends Shape def hasCorners(s: Shape): Boolean = s match - case hasCorners: Corners => true // <--- reported as `Unreachable case` + case hasCorners: Corners => true // <--- previously reported as `Unreachable case` (i21860), ok now (i11443) case _ => false class Test: