`Default.AsRecord` causes `StackOverflow` in a path-dependent type's companion object
justinp opened this issue · comments
Justin Patterson commented
I'm not sure if this is expected behavior or not, but it caused a bug deep within my code base that wasn't intuitive to me. The following code throws a StackOverflowError
.
import shapeless._
class DefaultsTest {
final case class A(a: Int = 9)
object A {
val d = Default.AsRecord[A].apply()
}
}
object DefaultsTest {
def main(args: Array[String]): Unit = {
val c = new DefaultsTest
println(c.A.d)
}
}
It works the way I expect if:
d
is declared as adef
instead of aval
A
is not path-dependentA.a
does not have a default valued
is declared outside ofA
's companion object
I'm using shapeless 2.3.3 on Scala 2.12.12.
Alexandre Archambault commented
@justinp What does the stacktrace look like?
Justin Patterson commented
Exception in thread "main" java.lang.StackOverflowError
at org.scalawag.bateman.jsonapi.generic.DefaultsTest.A$lzycompute$1(DefaultsTest.scala:6)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest.A(DefaultsTest.scala:6)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest$A$.<init>(DefaultsTest.scala:7)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest.A$lzycompute$1(DefaultsTest.scala:6)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest.A(DefaultsTest.scala:6)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest$A$.<init>(DefaultsTest.scala:7)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest.A$lzycompute$1(DefaultsTest.scala:6)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest.A(DefaultsTest.scala:6)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest$A$.<init>(DefaultsTest.scala:7)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest.A$lzycompute$1(DefaultsTest.scala:6)
at org.scalawag.bateman.jsonapi.generic.DefaultsTest.A(DefaultsTest.scala:6)
...
The AsRecord.apply()
is trying to reinitialize object A
.
Georgi Krastev commented
Minimisation with Default[A]
instead of Default.AsRecord[A]
Georgi Krastev commented
The difference between:
object A {
val x = 42
val d = x
}
and:
object A {
val x = 42
val d = A.x
}
Georgi Krastev commented
Note that in general we can't avoid a reference to A
:
object A {
val x = 42
object B {
val x = "shadow"
val d = A.x
}
}