Skip to content
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

Recursive references in objects #16456

Open
KuceraMartin opened this issue Dec 2, 2022 · 6 comments
Open

Recursive references in objects #16456

KuceraMartin opened this issue Dec 2, 2022 · 6 comments

Comments

@KuceraMartin
Copy link
Contributor

KuceraMartin commented Dec 2, 2022

Compiler version

3.2.0

Minimized code

The first example works (I believe) correctly. I would expect the latter two to have the same output, yet the second one results in stack overflow and the third one loops infinitely.

Example 1:

case object A:
  val b = B

case object B:
  val a = A

println(A)
println(A.b)
println(A.b.a)

https://scastie.scala-lang.org/TWhOPNLMTYyAV18EqK6hLA

Output

A
B
A

(this is correct)

Example 2:

{
  case object A:
    val b = B

  case object B:
    val a = A

  println(A)
  println(A.b)
  println(A.b.a)
}

https://scastie.scala-lang.org/Wki4Z9XJR3Gx13RFmCzpXQ

Output

Exception in thread "main" java.lang.StackOverflowError

Expectation

A
B
A

Example 3:

class MyClass:
  case object A:
    val b = B

  case object B:
    val a = A

val myClass = new MyClass()
println(myClass.A)
println(myClass.A.b)
println(myClass.A.b.a)

https://scastie.scala-lang.org/347k71gqSXuZhtZLOK69yQ

Output

(no output, loops infinitely)

Expectation

A
B
A
@KuceraMartin KuceraMartin added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Dec 2, 2022
@som-snytt
Copy link
Contributor

Compare #16152

The class initializer for the top-level object leaks its MODULE reference before it is initialized.

Local and member objects are lazy vals.

@olhotak
Copy link
Contributor

olhotak commented Oct 17, 2023

@liufengyun -Ysafe-init-global warns about this issue now.

It warns about example 2 but not about example 3.

@liufengyun
Copy link
Contributor

It warns about example 2 but not about example 3.

You mean the 1st example gets a warning? For the 2nd and 3rd, the objects are not global objects.

@olhotak
Copy link
Contributor

olhotak commented Oct 29, 2023

The 1st example gets a warning. We all agree on that.

The 2nd example doesn't even compile: { is Illegal start of toplevel definition. Scastie must wrap it somehow. I wrapped it in an outer object and then Ysafe-init-global gives a warning. How else is it intended to be wrapped?

For the 3rd example neither checker (neither -Ysafe-init nor -Ysafe-init-global) gives a warning, yet the code infinitely loops at run time.

@som-snytt
Copy link
Contributor

I took example 2 to mean local (not a member of a class or package).

➜  snips ll i*456*
-rw-r--r-- 1 amarki amarki 136 Dec  4  2022 i16456a.scala
-rw-r--r-- 1 amarki amarki 150 Dec  4  2022 i16456.scala
➜  snips cat i16456.scala

def f: Unit = {
  case object A:
    val b = B

  case object B:
    val a = A

  println(A)
  println(A.b)
  println(A.b.a)
}

@main def test() = f

Is example 3 a loop or rather deadlock on class initialization.

@liufengyun
Copy link
Contributor

Is example 3 a loop or rather deadlock on class initialization.

As it's given, there are no threads, so it's non-termination. If we access myClass.A and myClass.B from two different threads at the same time, then it might lead to a deadlock.

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

No branches or pull requests

5 participants