-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fixes #1856: mimic Scalac sematic of recursive lazy vals. #1892
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
Conversation
This change affects only non-threadsafe lazy vals. The change can be boined down to whether lazy vals do: flag = true
value_lzy = rhs or value_lzy = rhs
flag = true This difference wouldn't matter if lazy val wasn't recursive, but in case it is - it does matter. Dotty used to the first approach, and my(undocumented) rationale was that this ensures that lazy val is only assigned once. The second one instead may assign multiple times making object Test {
var count: Int = 0
def getLzy = {
val read = lzy
println("Lzy has been read as: " +read)
read
}
lazy val lzy: Int = {
if (count < 10) {
println(s"Iteration $count")
count += 1
getLzy + 1
} else 42
}
def main(args: Array[String]): Unit = {
println(lzy)
getLzy
}
} with the new Dotty behavior(and the Scalac one) this test prints:
As you can see in the test and output, you can observe multiple different values of a lazy val. Note that Lazy Vals Sip considers lazy vals with cycles to be errorneous code and does not address them. Dotty's thread safe lazy vals would wait forever on such a test, which I believe is better, as they would otherwise publish multiple values in a threaded environment. |
Continuing discussion on migration: as rewrite tool would stamp |
Thanks for looking into this! I think for now we should go with the scalac behavior since bootstrapping dotty relies on that and I'd like to get my full bootstrapping tests in (see #1856). But after merging this we should keep an issue open to consider alternative semantics.
That alleviates one of my concern, but if lazy vals can potentially return @DarkDimius: Maybe we should measure the performance impact of throwing an exception when a lazy val is called recursively? |
Random thought: when a lazy val is initialized twice, throw an exception only if it's initialized to a different value each time, otherwise don't do anything. This way you can be sure that the value you read from your lazy val is the final, correct value. |
… be assigned twice.
@smarter, for your curiosity, last commit implements the change that you've proposed. I quickly measured how much it would influence bytecode size: +50 bytes for every non-volatile lazy val. I don't think it's worth it though, for the same reason:
|
Maybe we could have an |
I agree that recursive lazy vals should be an error - ideally there would be a way to report such an error, maybe controlled by an option, if it's too expensive to do by default. Is there no way we can avoid the cycle in Dotty bootstrap? |
1 similar comment
I agree that recursive lazy vals should be an error - ideally there would be a way to report such an error, maybe controlled by an option, if it's too expensive to do by default. Is there no way we can avoid the cycle in Dotty bootstrap? |
It seems to me there is an easy way to avoid the problem in the bootstrap. Don't use a lazy val in ImportInfo. Use a cache var and a getter that implements the necessary logic. |
Yes, that works, though I had to replace both |
@smarter Great! Let's do that. |
So in the light of the discussion this PR should be closed, right? |
Sure. |
EDIT: moved to #1856. |
No description provided.