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

Re-initialization of transient lazy val not working after de-serialization #10244

Closed
scabug opened this issue Mar 22, 2017 · 8 comments
Closed
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Mar 22, 2017

Accessing a transient lazy val from a trait after an object was de-serialized causes a NullPointerException if the lazy val was accessed before the object was serialized.

One can reproduce this problem with the following classes:

class NotSerializable {
  def foo = "bar"
}

trait Base extends Serializable {
  @transient
  protected lazy val notSerializable = new NotSerializable
}

class BaseImpl extends Base {
  def check: Boolean = Try(notSerializable.foo).isSuccess
}

If you create an instance of BaseImpl, serialize and de-serialize this instance calling check works without problems.
If you create an instance, call check, serialize and de-serialize this instance calling check causes a NullPointerException.

@scabug
Copy link
Author

scabug commented Mar 22, 2017

Imported From: https://issues.scala-lang.org/browse/SI-10244?orig=1
Reporter: Benjamin Hagemeister (behag)
Affected Versions: 2.12.1
See #10075

@scabug
Copy link
Author

scabug commented Mar 22, 2017

Benjamin Hagemeister (behag) said:
This is probably related to #10075.

@sarahgerweck
Copy link

sarahgerweck commented Jun 8, 2017

The issue here is that the fix in #10075 marked the actual member as transient, but any transient fields need to have their associated bitmap marked as transient as well.

To properly fix this, @transient lazy val and lazy val need to use strictly separate bitmaps. Bitmaps in the former category need to be also be transient. Bitmaps in the latter must not be transient.

@sarahgerweck
Copy link

@adriaanm any thoughts? It looks to me like the code is trying to do the right thing, but it is not realizing that these values need to use the transient bitmap. This has just become a supermassive headache for me, so I'd love it if there were a fix (or at least a diagnosis that could lead somebody to fix it themselves).

@adriaanm adriaanm self-assigned this Jun 8, 2017
@adriaanm adriaanm added this to the 2.12.3 milestone Jun 8, 2017
@adriaanm
Copy link
Contributor

adriaanm commented Jun 8, 2017

Thanks for the analysis! I'll see if I can fix this in 2.12.3

@sarahgerweck
Copy link

sarahgerweck commented Jun 8, 2017

@adriaanm in case it's helpful, this is the line that seems to be trying to correctly decide between the two bitmaps: https://github.com/scala/scala/blob/2.12.x/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala#L134

I did notice that there's a bit of stripping and re-adding of annotations in #5570, and wondered if maybe the transient information isn't present when this is called, but that's about as far as I got in tracking this down.

@adriaanm
Copy link
Contributor

adriaanm commented Jun 9, 2017

wondered if maybe the transient information isn't present when this is called

Yep, the logic operates on the getters (which don't have the transient annotation) and then tries to link these to the field (which do have the expected annotation). The problem is that we cannot use accessed to get to the field from the getter in a class where these fields are inherited from a trait, because we are in the process of transforming the info of the class to add the members and their additional symbols as inherited from any supertraits, and are about to enter them into the class's decls (in its info, a ClassInfoType), but have not yet done so. Because the decls have not yet been entered, accessedOrSelf in def hasTransientAnnot(field: Symbol) = field.accessedOrSelf hasAnnotation TransientAttr returns NoSymbol.

@adriaanm
Copy link
Contributor

/cc @patriknw, as this bug affected akka cluster, based on my bytecode diffing (see PR for details)

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