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

transient lazy vals should not null constructor fields #8453

Closed
scabug opened this issue Mar 28, 2014 · 4 comments
Closed

transient lazy vals should not null constructor fields #8453

scabug opened this issue Mar 28, 2014 · 4 comments

Comments

@scabug
Copy link

scabug commented Mar 28, 2014

The following demonstrates expected and allowable behavior of the Scala compiler:

import java.io._

class MyClass(privateField: String) {
  lazy val lazyField = privateField
}

object MyClass {
  def main(args: Array[String]) {
    val instance = new MyClass("test")
    val privateField = classOf[MyClass].getDeclaredField("privateField")
    privateField.setAccessible(true)

    println("Path string: " + privateField.get(instance))
    // Prints "test"

    instance.lazyField

    println("Path string: " + privateField.get(instance))
    // Prints null
  }
}

The Scala compiler explicitly generates bytecode which sets privateField to null after initializing the lazy val because it knows that no one has access to privateField after this point. This is unintuitive, but doesn't actually violate correctness.

The problem comes in when the lazy val is marked @transient, because then the lazy val may be recomputed later and require the field value to exist. Intuitively, the following should work:

class MyClass(pathString: String) extends Serialized {
  @transient lazy val path: Path = new Path(pathString)
}

However, if we have the following access pattern:

val instance0 = new MyClass("/hello")
println("instance0 path = " + instance0.path)
val instance1 = serializeAndDeserialize(instance0)
println("instance1 path = " + instance1.path) // NullPointerException thrown

This is because pathString was set to null after the first lazy val access, and thus was serialized as null. When the val was recomputed after deserialization, it naturally failed.

This issue has the simple workaround of labeling the parameter "private val", but the behavior does not seem correct.

@scabug
Copy link
Author

scabug commented Mar 28, 2014

Imported From: https://issues.scala-lang.org/browse/SI-8453?orig=1
Reporter: Aaron Davidson (aarondav)
Affected Versions: 2.10.4

@scabug
Copy link
Author

scabug commented Mar 31, 2014

Aaron Davidson (aarondav) said:
Actually, it seems that labeling the parameter "private val" does not fix this behavior, since the compiler still thinks that no one will reference the field later and nulls it. The only workaround therefore is to use the field for some other purpose or to increase its visibility, neither of which is an ideal workaround.

@SethTisue
Copy link
Member

not sure why this has the "optimizer" label? is the issue only reproducible with the optimizer enabled? is it reproducible at all in 2.12?

@hrhino
Copy link
Member

hrhino commented Mar 2, 2018

Pretty sure this is a duplicate of #9365, actually.

@hrhino hrhino closed this as completed Mar 2, 2018
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

3 participants