Skip to content

Add support for lambda serialization #5837

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

Merged
merged 6 commits into from
Feb 6, 2019

Conversation

smarter
Copy link
Member

@smarter smarter commented Feb 3, 2019

See also the backend PR: lampepfl/scala#39

Copy link
Member

@dottybot dottybot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, and thank you for opening this PR! 🎉

All contributors have signed the CLA, thank you! ❤️

Commit Messages

We want to keep history, but for that to actually be useful we have
some rules on how to format our commit messages (relevant xkcd).

Please stick to these guidelines for commit messages:

  1. Separate subject from body with a blank line
  2. When fixing an issue, start your commit message with Fix #<ISSUE-NBR>:
  3. Limit the subject line to 72 characters
  4. Capitalize the subject line
  5. Do not end the subject line with a period
  6. Use the imperative mood in the subject line ("Add" instead of "Added")
  7. Wrap the body at 80 characters
  8. Use the body to explain what and why vs. how

adapted from https://chris.beams.io/posts/git-commit

Have an awesome day! ☀️

@smarter
Copy link
Member Author

smarter commented Feb 3, 2019

/cc @retronym @lrytz: could you double-check that this looks sane ? Also are there more test cases I should add ?

@smarter smarter force-pushed the serialize-lambdas branch 4 times, most recently from e50de50 to beaed8c Compare February 3, 2019 17:49
Copy link
Contributor

@OlivierBlanvillain OlivierBlanvillain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add a test for the implicit functions & dependent functions?

smarter and others added 6 commits February 6, 2019 16:16
To avoid deadlocks when combining objects, lambdas and multi-threading,
lambdas in objects are compiled to instance methods of the module class
instead of static methods (see tests/run/deadlock.scala and
scala/scala-dev#195 for details).

This has worked well for us so far but this is problematic for
serialization: serializing a lambda requires serializing all the values
it captures, if this lambda is in an object, this means serializing the
enclosing object, which fails if the object does not extend
Serializable.

Because serializing objects is basically free since scala#5775, it seems like
the simplest solution is to simply make all objects Serializable, this
certainly seems preferable to deadlocks.

This commit causes a cyclic reference to happen in some cases, we add a
workaround to avoid this in Trees.scala and fix it properly in the
commit.
The previous commit added _root_.scala.Serializable as a parent to every
object, this lead to a cyclic reference manifesting itselfs as "not
found: type Type" in Trees.scala that we worked around by replace `Type`
with `Types.Type`. This commit fixes this properly by adding a shortcut
in typedIdent when typing `_root_` which avoids forcing imports.
These interfaces are only used when creating closures with no explicit
SAM types, and those closures should always be serializable.
…for FunctionXXL

This only makes a difference after erasure, and I believe this is less
surprising than the previous behavior.

This necessitated a slight refactoring in GenericSignature to avoid an
infinite loop now that `isXXLFunctionClass` returns true for
scala.FunctionXXL and not just for the classes that erase to scala.FunctionXXL.
In Scala, lambdas whose SAM extend Serializable as well as lambdas whose
SAM is simply scala.Function* should be serializable but this was not
the case in Dotty so far. On the JVM, lambdas instantiated using
invokedynamic calls require some special handling to be serializable:

1. We need to use the invokedynamic bootstrap method
   `LambdaMetaFactory#altMetafactory` instead of
   `LambdaMetaFactory#metafactory`, this allows us to pass the
   FLAG_SERIALIZABLE flag. This is implemented in the backend submodule
   commit included in this commit (see lampepfl/scala#39).

2. In the enclosing class where the lambda is defined, a
   $deserializeLambda$ method needs to be generated, this is implemented
   in this commit.

Most of the logic for $deserializeLambda$ is implemented in the Scala
2.12 standard libraries class scala.runtime.LambdaDeserialize and
scala.runtime.LambdaDeserializer which can be used here as-is, the only
logic we actually need to implement here is:

1. In `collectSerializableLambdas`, we collect all serializable lambdas.
   Unlike scalac, our backend does not do any inlining currently so our
   implementation is more straightfoward than theirs.

2. In `addLambdaDeserialize`, we implement the actual
   $deserializeLambda$ method, the implementation here is directly
   copied from scalac, it's complex because it needs to work around a
   limitation of bootstrap methods (they cannot take more than 251
   arguments).

Since some of this code comes from scalac, this is:
Co-Authored-By: Jason Zaugg <[email protected]>
Co-Authored-By: Lukas Rytz <[email protected]>
@smarter smarter merged commit 56ca9ab into scala:master Feb 6, 2019
@allanrenucci allanrenucci deleted the serialize-lambdas branch February 6, 2019 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants