Giter VIP home page Giter VIP logo

Comments (10)

bishabosha avatar bishabosha commented on September 24, 2024

the reason this happens is because you are initialising A in an infinite loop, the Mirror of class A is object A, so you are trying to store A in a field in its own body.

exactly like you did this:

def foo() =
  object Foo:
    val mirror = Foo
  println(Foo.mirror)

foo()

from scala3.

mrdziuban avatar mrdziuban commented on September 24, 2024

I see, thanks for explaining @bishabosha. Why does it work for non-local classes though?

from scala3.

bishabosha avatar bishabosha commented on September 24, 2024

the solution here could be a linter that checks for these kinds of cases. (maybe @liufengyun knows)?

from scala3.

mrdziuban avatar mrdziuban commented on September 24, 2024

For context, this came up in circe/circe#2263. It seems like disallowing cases like this would also mean derivation would no longer be possible, which feels less than ideal.

from scala3.

bishabosha avatar bishabosha commented on September 24, 2024

can use a lazy val?

from scala3.

mrdziuban avatar mrdziuban commented on September 24, 2024

Yeah using a lazy val works as a workaround but in the context of derivation there's no way to make sure all downstream users do so

from scala3.

bishabosha avatar bishabosha commented on September 24, 2024

well is it possible to delay the evaluation of the mirror in the circe deriving code since you are retaining the mirror?

from scala3.

mrdziuban avatar mrdziuban commented on September 24, 2024

That sounds promising, but I've tried a number of things and can't get it working. The only fix I've found so far is twofold:

  1. Mark the mirror param here as inline -- https://github.com/circe/circe/blob/v0.14.7/modules/core/shared/src/main/scala-3/io/circe/derivation/ConfiguredDecoder.scala#L222
  2. Construct the ConfiguredDecoder in the inline method rather than proxying to ConfiguredDecoder.of

Both of these have their own issues though:

  1. This requires downgrading Scala to 3.2.2, which in turn requires downgrading sbt and cats. In Scala 3.3 and later, the inline mirror results in errors like this:
        -- [E083] Type Error: /Users/matt/circe/modules/core/shared/src/main/scala-3/io/circe/derivation/ConfiguredDecoder.scala:223:39
    223 |    ConfiguredDecoder.of[A](constValue[mirror.MirroredLabel], decoders[A], summonLabels[mirror.MirroredElemLabels])
        |                                       ^^^^^^
        |(mirror : scala.deriving.Mirror.Of[A]) is not a valid type prefix, since it is not an immutable path
  2. This was purposely changed recently to avoid generating a new class file per call to derived -- circe/circe#2230

Here's my WIP branch in case you see anything I might be missing: circe/circe@series/0.14.x...mblink:fix-2263

from scala3.

mrdziuban avatar mrdziuban commented on September 24, 2024

I'm far from an expert in interpreting how this might be related, but in case it helps someone else here's the output and diff of compiling a top-level vs. local class with -Xprint:genBCode

top-level-class-output.txt
local-class-output.txt

// top-level-class.scala
case object A {
  val mirror = summon[scala.deriving.Mirror.ProductOf[A.type]]
}
// local-class.scala
def test(): Unit = {
  case object A {
    val mirror = summon[scala.deriving.Mirror.ProductOf[A.type]]
  }
}

I can follow why there's a stack overflow with the local version -- during the initialization of the lazy val, the class constructor calls rs$line$1$$$_$A$1, which calls A$lzyINIT1$1, which calls the class constructor, etc.

Other notable differences (to me at least) are:

  1. case object A is represented as a module case class in the top-level version as opposed to a case class in the local version
  2. The class constructor takes a LazyRef representing an instance of the class itself
  3. val mirror is represented as a private <static> val in the top-level version as opposed to a private val in the local version
diff --git a/Users/matt/Desktop/top-level-class-output.txt b/Users/matt/Desktop/local-class-output.txt
index c8c1fa23f6b..906c223349f 100644
--- a/Users/matt/Desktop/top-level-class-output.txt
+++ b/Users/matt/Desktop/local-class-output.txt
@@ -7,17 +7,33 @@ package <empty> {
       }
     private def writeReplace(): Object =
       new scala.runtime.ModuleSerializationProxy(classOf[rs$line$1])
-    final lazy module case <static> val A: rs$line$1$A = new rs$line$1$A()
+    def test(): Unit =
+      {
+        lazy var A$lzy1: scala.runtime.LazyRef = new scala.runtime.LazyRef()
+        ()
+      }
+    private final def A$lzyINIT1$1(A$lzy1$1: scala.runtime.LazyRef):
+      rs$line$1$A$2 =
+      A$lzy1$1.synchronized[rs$line$1$A$2](
+        (if A$lzy1$1.initialized() then A$lzy1$1.value() else
+          A$lzy1$1.initialize(new rs$line$1$A$2(A$lzy1$1))).asInstanceOf[
+          rs$line$1$A$2]
+      )
+    final lazy case def rs$line$1$$$_$A$1(A$lzy1$2: scala.runtime.LazyRef):
+      rs$line$1$A$2 =
+      (if A$lzy1$2.initialized() then A$lzy1$2.value() else
+        this.A$lzyINIT1$1(A$lzy1$2)).asInstanceOf[rs$line$1$A$2]
   }
-  final module case class rs$line$1$A extends Object, Product,
+  private final case class rs$line$1$A$2 extends Object, Product,
     java.io.Serializable, scala.deriving.Mirror.Mirror$Singleton {
-    def <init>(): Unit =
+    def <init>(A$lzy1$3: scala.runtime.LazyRef): Unit =
       {
         super()
-        mirror =
+        this.mirror =
           {
             val x$proxy1: scala.deriving.Mirror.Mirror$Singleton =
-              A:scala.deriving.Mirror.Mirror$Singleton
+              rs$line$1.this.rs$line$1$$$_$A$1(A$lzy1$3):
+                scala.deriving.Mirror.Mirror$Singleton
             x$proxy1
           }
         ()
@@ -28,12 +44,10 @@ package <empty> {
       super[Product].productElementNames()
     def fromProduct(p: Product): scala.deriving.Mirror.Mirror$Singleton =
       super[Singleton].fromProduct(p)
-    private def writeReplace(): Object =
-      new scala.runtime.ModuleSerializationProxy(classOf[rs$line$1$A])
     override def hashCode(): Int = 65
     override def toString(): String = "A"
     override def canEqual(that: Object): Boolean =
-      that.isInstanceOf[rs$line$1$A]
+      that.isInstanceOf[rs$line$1$A$2]
     override def productArity(): Int = 0
     override def productPrefix(): String = "A"
     override def productElement(n: Int): Object =
@@ -48,8 +62,8 @@ package <empty> {
           case val x2: Int = n
           throw new IndexOutOfBoundsException(Int.box(n).toString())
         }
-    private <static> val mirror: scala.deriving.Mirror.Mirror$Singleton
-    def mirror(): scala.deriving.Mirror.Mirror$Singleton = mirror
+    private val mirror: scala.deriving.Mirror.Mirror$Singleton
+    def mirror(): scala.deriving.Mirror.Mirror$Singleton = this.mirror
     def fromProduct(p: Product): Object = this.fromProduct(p)
   }
   final lazy module val rs$line$1: rs$line$1 = new rs$line$1()

from scala3.

liufengyun avatar liufengyun commented on September 24, 2024

the solution here could be a linter that checks for these kinds of cases. (maybe @liufengyun knows)?

Non-termination is not captured by the initialization checker, as we know accurately checking non-termination is impossible.

To soundly check non-termination, the checker would report many false positives, which will be annoying.

To address the problem above and related problems with simple non-termination, it seems an unsound check of blatant non-terminations that only report true positives will be useful in practice.

/cc: @olhotak @EnzeXing

from scala3.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.