Skip to content

Commit 4f94cce

Browse files
committed
Make all objects Serializable
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.
1 parent 45a51d8 commit 4f94cce

File tree

4 files changed

+41
-25
lines changed

4 files changed

+41
-25
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,9 @@ object desugar {
340340
case _ => false
341341
}
342342

343-
val isCaseClass = mods.is(Case) && !mods.is(Module)
344-
val isCaseObject = mods.is(Case) && mods.is(Module)
343+
val isObject = mods.is(Module)
344+
val isCaseClass = mods.is(Case) && !isObject
345+
val isCaseObject = mods.is(Case) && isObject
345346
val isImplicit = mods.is(Implicit)
346347
val isInstance = isImplicit && mods.mods.exists(_.isInstanceOf[Mod.Instance])
347348
val isEnum = mods.isEnumClass && !mods.is(Module)
@@ -533,6 +534,8 @@ object desugar {
533534
parents1 = enumClassTypeRef :: Nil
534535
if (isCaseClass | isCaseObject)
535536
parents1 = parents1 :+ scalaDot(str.Product.toTypeName) :+ scalaDot(nme.Serializable.toTypeName)
537+
else if (isObject)
538+
parents1 = parents1 :+ scalaDot(nme.Serializable.toTypeName)
536539
if (isEnum)
537540
parents1 = parents1 :+ ref(defn.EnumType)
538541

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,8 @@ object Trees {
887887

888888
// ----- Generic Tree Instances, inherited from `tpt` and `untpd`.
889889

890-
abstract class Instance[T >: Untyped <: Type] { inst =>
890+
// FIXME: Work around cyclic reference by writing `Types.Type` instead of `Type`
891+
abstract class Instance[T >: Untyped <: Types.Type] { inst =>
891892

892893
type Tree = Trees.Tree[T]
893894
type TypTree = Trees.TypTree[T]

sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ class ExtractUsedNamesSpecification {
7979
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB, srcC, srcD)
8080
val scalaVersion = scala.util.Properties.versionNumberString
8181
val namesA = standardNames ++ Set("Nothing", "Any")
82-
val namesAX = standardNames ++ Set("x", "T", "A", "Nothing", "Any")
82+
val namesAX = standardNames ++ objectStandardNames ++ Set("x", "T", "A", "Nothing", "Any", "scala")
8383
val namesB = Set("A", "Int", "A;init;", "Unit")
84-
val namesC = Set("B;init;", "B", "Unit")
85-
val namesD = standardNames ++ Set("C", "X", "foo", "Int", "T")
84+
val namesC = objectStandardNames ++ Set("B;init;", "B", "Unit")
85+
val namesD = standardNames ++ objectStandardNames ++ Set("C", "X", "foo", "Int", "T")
8686
assertEquals(namesA, usedNames("A"))
8787
assertEquals(namesAX, usedNames("A.X"))
8888
assertEquals(namesB, usedNames("B"))
@@ -130,23 +130,30 @@ class ExtractUsedNamesSpecification {
130130
|""".stripMargin
131131
val compilerForTesting = new ScalaCompilerForUnitTesting
132132
val usedNames = compilerForTesting.extractUsedNamesFromSrc(src1, src2)
133-
val expectedNames_lista = standardNames ++ Set("B", "lista", "List", "A")
134-
val expectedNames_at = standardNames ++ Set("B", "at", "A", "T", "X0", "X1")
135-
val expectedNames_as = standardNames ++ Set("B", "as", "S", "Y")
136-
val expectedNames_foo = standardNames ++ Set("B",
137-
"foo",
138-
"M",
139-
"N",
140-
"Predef",
141-
"???",
142-
"Nothing")
143-
val expectedNames_bar = standardNames ++ Set("B",
144-
"bar",
145-
"P1",
146-
"P0",
147-
"Predef",
148-
"???",
149-
"Nothing")
133+
val expectedNames_lista =
134+
standardNames ++ objectStandardNames ++ Set("B", "lista", "List", "A")
135+
val expectedNames_at =
136+
standardNames ++ objectStandardNames ++ Set("B", "at", "A", "T", "X0", "X1")
137+
val expectedNames_as =
138+
standardNames ++ objectStandardNames ++ Set("B", "as", "S", "Y")
139+
val expectedNames_foo =
140+
standardNames ++ objectStandardNames ++
141+
Set("B",
142+
"foo",
143+
"M",
144+
"N",
145+
"Predef",
146+
"???",
147+
"Nothing")
148+
val expectedNames_bar =
149+
standardNames ++ objectStandardNames ++
150+
Set("B",
151+
"bar",
152+
"P1",
153+
"P0",
154+
"Predef",
155+
"???",
156+
"Nothing")
150157
assertEquals(expectedNames_lista, usedNames("Test_lista"))
151158
assertEquals(expectedNames_at, usedNames("Test_at"))
152159
assertEquals(expectedNames_as, usedNames("Test_as"))
@@ -167,7 +174,7 @@ class ExtractUsedNamesSpecification {
167174
|""".stripMargin
168175
val compilerForTesting = new ScalaCompilerForUnitTesting
169176
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcFoo, srcBar)
170-
val expectedNames = standardNames ++ Set("Outer", "TypeInner", "Inner", "Int")
177+
val expectedNames = standardNames ++ objectStandardNames ++ Set("Outer", "TypeInner", "Inner", "Int")
171178
assertEquals(expectedNames, usedNames("Bar"))
172179
}
173180

@@ -302,4 +309,9 @@ class ExtractUsedNamesSpecification {
302309
// the return type of the default constructor is Unit
303310
"Unit"
304311
)
312+
313+
private val objectStandardNames = Set(
314+
// all Dotty objects extend scala.Serializable
315+
"scala", "Serializable"
316+
)
305317
}

0 commit comments

Comments
 (0)