Skip to content

Commit 17876ff

Browse files
committed
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 issue occurred because: 1. The enum case `Nn` is desugared to a lazy val without an explicit type 2. Type inference requires typing the RHS (an anonymous class) 3. Typing the parent `Comparable[Nn.type]` requires accessing `Nn`'s type 4. This triggers `Nn`'s completer while it's already being completed The fix detects self-references in enum case parents and provides an explicit type annotation (the first parent, which is always the enum type) to break the cycle. This allows the compiler to resolve `Nn.type` without triggering recursive completion. The explicit type is only added when there's actually a self-reference, so normal enum cases continue to have their full type inferred as before.
1 parent 23f5e32 commit 17876ff

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,27 @@ object DesugarEnums {
289289
val (tag, scaffolding) = nextOrdinal(name, CaseKind.Object, definesLookups)
290290
val impl1 = cpy.Template(impl)(parents = impl.parents :+ scalaRuntimeDot(tpnme.EnumValue), body = Nil)
291291
.withAttachment(ExtendsSingletonMirror, ())
292-
val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods.withAddedFlags(EnumValue, span))
292+
293+
// Check if any parent contains a reference to this enum case name
294+
def containsNameRef(tree: Tree): Boolean = tree match {
295+
case Ident(n) if n == name => true
296+
case Select(qual, _) => containsNameRef(qual)
297+
case AppliedTypeTree(tpt, args) => containsNameRef(tpt) || args.exists(containsNameRef)
298+
case SingletonTypeTree(ref) => containsNameRef(ref)
299+
case _ => false
300+
}
301+
302+
// Only provide explicit type if there's a self-reference to avoid cycles
303+
// (e.g., `case Nn extends Opt[Nothing] with Comparable[Nn.type]`)
304+
val tpt =
305+
if impl.parents.exists(containsNameRef) then
306+
// Use first parent (the enum type) as explicit type to break the cycle
307+
impl.parents.headOption.getOrElse(TypeTree())
308+
else
309+
// No self-reference, let the type be inferred normally
310+
TypeTree()
311+
312+
val vdef = ValDef(name, tpt, New(impl1)).withMods(mods.withAddedFlags(EnumValue, span))
293313
flatTree(vdef :: scaffolding).withSpan(span)
294314
}
295315
}

tests/pos/i11443.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
enum Opt[T] {
2+
case Nn extends Opt[Nothing] with Comparable[Nn.type]
3+
4+
def compareTo(nn: Nn.type) = 0
5+
}

0 commit comments

Comments
 (0)