From 33b989478c1b0ee8ce69691f9dde427ba94d8dab Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 25 Jan 2019 21:24:58 +0100 Subject: [PATCH] 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 https://github.com/scala/scala-dev/issues/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 #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. --- .../src/dotty/tools/dotc/ast/Desugar.scala | 9 ++-- compiler/src/dotty/tools/dotc/ast/Trees.scala | 3 +- .../xsbt/ExtractUsedNamesSpecification.scala | 54 +++++++++++-------- scala-backend | 2 +- 4 files changed, 41 insertions(+), 27 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index df3809b64281..dd15a25031d5 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -341,8 +341,9 @@ object desugar { case _ => false } - val isCaseClass = mods.is(Case) && !mods.is(Module) - val isCaseObject = mods.is(Case) && mods.is(Module) + val isObject = mods.is(Module) + val isCaseClass = mods.is(Case) && !isObject + val isCaseObject = mods.is(Case) && isObject val isImplicit = mods.is(Implicit) val isInstance = isImplicit && mods.mods.exists(_.isInstanceOf[Mod.Instance]) val isEnum = mods.isEnumClass && !mods.is(Module) @@ -527,13 +528,13 @@ object desugar { else Nil } - // Case classes and case objects get Product parents - // Enum cases get an inferred parent if no parents are given var parents1 = parents if (isEnumCase && parents.isEmpty) parents1 = enumClassTypeRef :: Nil if (isCaseClass | isCaseObject) parents1 = parents1 :+ scalaDot(str.Product.toTypeName) :+ scalaDot(nme.Serializable.toTypeName) + else if (isObject) + parents1 = parents1 :+ scalaDot(nme.Serializable.toTypeName) if (isEnum) parents1 = parents1 :+ ref(defn.EnumType) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 6cc697bfb5d6..1d20f20e5d89 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -887,7 +887,8 @@ object Trees { // ----- Generic Tree Instances, inherited from `tpt` and `untpd`. - abstract class Instance[T >: Untyped <: Type] { inst => + // FIXME: Work around cyclic reference by writing `Types.Type` instead of `Type` + abstract class Instance[T >: Untyped <: Types.Type] { inst => type Tree = Trees.Tree[T] type TypTree = Trees.TypTree[T] diff --git a/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala b/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala index befe2dd433d3..9106d9f5304a 100644 --- a/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala +++ b/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala @@ -79,10 +79,10 @@ class ExtractUsedNamesSpecification { val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB, srcC, srcD) val scalaVersion = scala.util.Properties.versionNumberString val namesA = standardNames ++ Set("Nothing", "Any") - val namesAX = standardNames ++ Set("x", "T", "A", "Nothing", "Any") + val namesAX = standardNames ++ objectStandardNames ++ Set("x", "T", "A", "Nothing", "Any", "scala") val namesB = Set("A", "Int", "A;init;", "Unit") - val namesC = Set("B;init;", "B", "Unit") - val namesD = standardNames ++ Set("C", "X", "foo", "Int", "T") + val namesC = objectStandardNames ++ Set("B;init;", "B", "Unit") + val namesD = standardNames ++ objectStandardNames ++ Set("C", "X", "foo", "Int", "T") assertEquals(namesA, usedNames("A")) assertEquals(namesAX, usedNames("A.X")) assertEquals(namesB, usedNames("B")) @@ -130,23 +130,30 @@ class ExtractUsedNamesSpecification { |""".stripMargin val compilerForTesting = new ScalaCompilerForUnitTesting val usedNames = compilerForTesting.extractUsedNamesFromSrc(src1, src2) - val expectedNames_lista = standardNames ++ Set("B", "lista", "List", "A") - val expectedNames_at = standardNames ++ Set("B", "at", "A", "T", "X0", "X1") - val expectedNames_as = standardNames ++ Set("B", "as", "S", "Y") - val expectedNames_foo = standardNames ++ Set("B", - "foo", - "M", - "N", - "Predef", - "???", - "Nothing") - val expectedNames_bar = standardNames ++ Set("B", - "bar", - "P1", - "P0", - "Predef", - "???", - "Nothing") + val expectedNames_lista = + standardNames ++ objectStandardNames ++ Set("B", "lista", "List", "A") + val expectedNames_at = + standardNames ++ objectStandardNames ++ Set("B", "at", "A", "T", "X0", "X1") + val expectedNames_as = + standardNames ++ objectStandardNames ++ Set("B", "as", "S", "Y") + val expectedNames_foo = + standardNames ++ objectStandardNames ++ + Set("B", + "foo", + "M", + "N", + "Predef", + "???", + "Nothing") + val expectedNames_bar = + standardNames ++ objectStandardNames ++ + Set("B", + "bar", + "P1", + "P0", + "Predef", + "???", + "Nothing") assertEquals(expectedNames_lista, usedNames("Test_lista")) assertEquals(expectedNames_at, usedNames("Test_at")) assertEquals(expectedNames_as, usedNames("Test_as")) @@ -167,7 +174,7 @@ class ExtractUsedNamesSpecification { |""".stripMargin val compilerForTesting = new ScalaCompilerForUnitTesting val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcFoo, srcBar) - val expectedNames = standardNames ++ Set("Outer", "TypeInner", "Inner", "Int") + val expectedNames = standardNames ++ objectStandardNames ++ Set("Outer", "TypeInner", "Inner", "Int") assertEquals(expectedNames, usedNames("Bar")) } @@ -302,4 +309,9 @@ class ExtractUsedNamesSpecification { // the return type of the default constructor is Unit "Unit" ) + + private val objectStandardNames = Set( + // all Dotty objects extend scala.Serializable + "scala", "Serializable" + ) } diff --git a/scala-backend b/scala-backend index 9ef70dd9b9ee..1447a7fbbc5d 160000 --- a/scala-backend +++ b/scala-backend @@ -1 +1 @@ -Subproject commit 9ef70dd9b9eeec11cfb72dabb57c61198fa18a20 +Subproject commit 1447a7fbbc5dd51a5e561c57bc873576e1ba542c