Skip to content

Illegal cyclic reference - regression from Scala 2 #19618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
errt opened this issue Feb 5, 2024 · 2 comments · May be fixed by #20236
Closed

Illegal cyclic reference - regression from Scala 2 #19618

errt opened this issue Feb 5, 2024 · 2 comments · May be fixed by #20236

Comments

@errt
Copy link

errt commented Feb 5, 2024

Compiler version

3.3.1

Minimized code

trait A {
  type T1
  type T2 <: T1
}

trait B extends A {
  final override type T1 = T2
  final override type T2 = Int
}

trait C extends A

trait D extends B with C { self => }

class E extends D

Output

[error] 15 |class E extends D
[error]    |      ^
[error]    |Recursion limit exceeded.
[error]    |Maybe there is an illegal cyclic reference?
[error]    |If that's not the case, you could also try to increase the stacksize using the -Xss JVM option.
[error]    |For the unprocessed stack trace, compile with -Yno-decode-stacktraces.
[error]    |A recurring operation is (inner to outer):
[error]    |
[error]    |  find-member .T1
[error]    |  find-member .T2
[error]    |  ...
[error]    |
[error]    |  find-member .T2
[error]    |  find-member .T1

Expectation

The code compiles with Scala 2, but not with Dotty.

Removing C fixes it in the MWE above, but is not possible in the original code where C has actual members.
Changing T1 in B to final override type T1 = Int prevents the bug, but was not necessary with Scala 2.

@errt errt added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Feb 5, 2024
@Gedochao Gedochao added compat:scala2 area:typer regression:scala2 and removed stat:needs triage Every issue needs to have an "area" and "itype" label compat:scala2 labels Feb 8, 2024
@odersky
Copy link
Contributor

odersky commented Feb 19, 2024

Workaround

trait A {
  type T1
  type T2 <: T1
}

trait B extends A {
  final override type T1 = Int // was T2
  final override type T2 = Int
}

trait C extends A

trait D extends B with C { self => }

class E extends D

In a sense the message makes sense. If we overlay what is defined in A and B we get T1 = T2 and T2 <: T1, hence T2 <: T2. That's a cycle. I don't think it's worth fixing in particular because the workaround is trivial. @dwijnand do you agree?

@dwijnand
Copy link
Member

That's a cycle.

Actually, yes, I agree. I would even suggest the workaround that's perhaps closer to the intent is:

  final override type T1 = Int
  final override type T2 = T1

which works. I think we would need to see some slightly less minimised, more real-world example that can demonstrate why this should be made to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants