Comments (10)
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.
I see, thanks for explaining @bishabosha. Why does it work for non-local classes though?
from scala3.
the solution here could be a linter that checks for these kinds of cases. (maybe @liufengyun knows)?
from scala3.
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.
can use a lazy val?
from scala3.
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.
well is it possible to delay the evaluation of the mirror in the circe deriving code since you are retaining the mirror?
from scala3.
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:
- Mark the
mirror
param here asinline
-- https://github.com/circe/circe/blob/v0.14.7/modules/core/shared/src/main/scala-3/io/circe/derivation/ConfiguredDecoder.scala#L222 - Construct the
ConfiguredDecoder
in theinline
method rather than proxying toConfiguredDecoder.of
Both of these have their own issues though:
- 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
- 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.
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:
case object A
is represented as amodule case class
in the top-level version as opposed to acase class
in the local version- The class constructor takes a
LazyRef
representing an instance of the class itself val mirror
is represented as aprivate <static> val
in the top-level version as opposed to aprivate 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.
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.
from scala3.
Related Issues (20)
- Can't build or test locally HOT 12
- Regression in `playframework/playframework` for access to private static class public members HOT 6
- [Macros] `-Ycheck:all` preventing a legit method definition HOT 2
- Implement correct capture check for lazy vals
- Nightly Dotty workflow of 2024-09-18 failed
- 3.5.1, 3.5.2-RC1 and 3.3.4-RC4 Release procedure
- Getting Started guide doesn't work on Windows HOT 2
- Defining and Transforming Capabilities from other Capabilities HOT 8
- ClassCastException in Quotes' extractor
- Pattern matching against `enum` case without parameters compiles without warnings HOT 9
- -Yexplicit-nulls exhibits false negative when combining try/match HOT 1
- Captures of curried function are incorrectly dropped
- Heisenbug corruption/loss of surrounding tree with `-Yretain-trees` when a transparent macro with an implicit parameter is executed in the same file
- Invalid variance inferred / variance check failed HOT 2
- Consider nullable annotations in explicit nulls HOT 4
- Unable to extend protected nested class HOT 1
- Type infererence regression in `pityka/lamp` HOT 2
- Quoted type pattern for `'[Map[k, v]]` doesn't match when `k` is an `OrType`
- scala.MatchError during parsing HOT 1
- Strange Cyclic reference error with incremental compilation
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from scala3.