Skip to content

Fix/initializer deadlocks #628

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 8 commits into from
Jun 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ class Compiler {
new Constructors,
new FunctionalInterfaces),
List(new LambdaLift, // in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
new Flatten,
new ElimStaticThis,
new Flatten,
new RestoreScopes),
List(/*new PrivateToStatic,*/
new ExpandPrivate,
Expand Down
3 changes: 3 additions & 0 deletions src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ object Contexts {
final def withPhase(phase: Phase): Context =
withPhase(phase.id)

final def withPhaseNoLater(phase: Phase) =
if (ctx.phase.id > phase.id) withPhase(phase) else ctx

/** If -Ydebug is on, the top of the stack trace where this context
* was created, otherwise `null`.
*/
Expand Down
11 changes: 3 additions & 8 deletions src/dotty/tools/dotc/transform/ElimStaticThis.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.SymDenotations.SymDenotation
import TreeTransforms.{MiniPhaseTransform, TransformerInfo}
import dotty.tools.dotc.core.Types.{ThisType, TermRef}
import Phases.Phase

/** Replace This references to module classes in static methods by global identifiers to the
* corresponding modules.
Expand All @@ -18,8 +17,6 @@ class ElimStaticThis extends MiniPhaseTransform {
import ast.tpd._
def phaseName: String = "elimStaticThis"

override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Flatten])

override def transformThis(tree: This)(implicit ctx: Context, info: TransformerInfo): Tree =
if (!tree.symbol.is(Package) && ctx.owner.enclosingMethod.is(JavaStatic)) {
assert(tree.symbol.is(ModuleClass))
Expand All @@ -28,12 +25,10 @@ class ElimStaticThis extends MiniPhaseTransform {
else tree

override def transformIdent(tree: tpd.Ident)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
val meth = ctx.owner.enclosingMethod
// We cannot use meth.enclosingClass because it skips other static classes,
// so instead we require this phase to run after Flatten and use meth.owner
if (meth.is(JavaStatic) && meth.owner.is(ModuleClass)) {
if (ctx.owner.enclosingMethod.is(JavaStatic)) {
tree.tpe match {
case TermRef(thiz: ThisType, _) if (thiz.underlying.typeSymbol == meth.owner) =>
case TermRef(thiz: ThisType, _) =>
assert(thiz.underlying.typeSymbol.is(ModuleClass))
ref(thiz.underlying.typeSymbol.sourceModule).select(tree.symbol)
case _ => tree
}
Expand Down
5 changes: 3 additions & 2 deletions src/dotty/tools/dotc/transform/ExplicitOuter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,10 @@ object ExplicitOuter {
def path(toCls: Symbol): Tree = try {
def loop(tree: Tree): Tree = {
val treeCls = tree.tpe.widen.classSymbol
ctx.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${outerAccName(treeCls.asClass)}")
val outerAccessorCtx = ctx.withPhaseNoLater(ctx.lambdaLiftPhase) // lambdalift mangles local class names, which means we cannot reliably find outer acessors anymore
ctx.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${outerAccName(treeCls.asClass)(outerAccessorCtx)} in $treeCls")
if (treeCls == toCls) tree
else loop(tree select outerAccessor(treeCls.asClass))
else loop(tree select outerAccessor(treeCls.asClass)(outerAccessorCtx))
}
ctx.log(i"computing outerpath to $toCls from ${ctx.outersIterator.map(_.owner).toList}")
loop(This(ctx.owner.enclosingClass.asClass))
Expand Down
22 changes: 14 additions & 8 deletions src/dotty/tools/dotc/transform/LambdaLift.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
* than the previous value of `liftedowner(sym)`.
*/
def narrowLiftedOwner(sym: Symbol, owner: Symbol)(implicit ctx: Context) = {
if (sym.owner.isTerm &&
if (sym.maybeOwner.isTerm &&
owner.isProperlyContainedIn(liftedOwner(sym)) &&
owner != sym) {
ctx.log(i"narrow lifted $sym to $owner")
Expand Down Expand Up @@ -189,10 +189,9 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
val sym = tree.symbol
def narrowTo(thisClass: ClassSymbol) = {
val enclClass = enclosure.enclosingClass
if (!thisClass.isStaticOwner)
narrowLiftedOwner(enclosure,
if (enclClass.isContainedIn(thisClass)) thisClass
else enclClass) // unknown this reference, play it safe and assume the narrowest possible owner
narrowLiftedOwner(enclosure,
if (enclClass.isContainedIn(thisClass)) thisClass
else enclClass) // unknown this reference, play it safe and assume the narrowest possible owner
}
tree match {
case tree: Ident =>
Expand All @@ -210,8 +209,15 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
case tree: This =>
narrowTo(tree.symbol.asClass)
case tree: DefDef =>
if (sym.owner.isTerm && !sym.is(Label)) liftedOwner(sym) = sym.topLevelClass.owner
else if (sym.isPrimaryConstructor && sym.owner.owner.isTerm) symSet(called, sym) += sym.owner
if (sym.owner.isTerm && !sym.is(Label))
liftedOwner(sym) = sym.enclosingClass.topLevelClass
// this will make methods in supercall constructors of top-level classes owned
// by the enclosing package, which means they will be static.
// On the other hand, all other methods will be indirectly owned by their
// top-level class. This avoids possible deadlocks when a static method
// has to access its enclosing object from the outside.
else if (sym.isPrimaryConstructor && sym.owner.owner.isTerm)
symSet(called, sym) += sym.owner
case tree: TypeDef =>
if (sym.owner.isTerm) liftedOwner(sym) = sym.topLevelClass.owner
case tree: Template =>
Expand Down Expand Up @@ -360,7 +366,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
val clazz = sym.enclosingClass
val qual =
if (clazz.isStaticOwner) singleton(clazz.thisType)
else outer(ctx.withPhase(thisTransform)).path(clazz)
else outer.path(clazz)
transformFollowingDeep(qual.select(sym))
}

Expand Down
20 changes: 20 additions & 0 deletions tests/pending/pos/lambdalift-1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class Super(x: Int)

class Sub extends Super({
def foo3(x: Int) = {

class C {
def this(name: String) = this()

def bam(y: Int): String => Int = {
def baz = x + y
z => baz * z.length
}
}

val fun = new C("dummy").bam(1)
fun("abc")

}
foo3(22)
})
21 changes: 0 additions & 21 deletions tests/pos/lambdalift.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,3 @@ object test {

}
}

class Super(x: Int)

class Sub extends Super({
def foo3(x: Int) = {

class C {
def this(name: String) = this()

def bam(y: Int): String => Int = {
def baz = x + y
z => baz * z.length
}
}

val fun = new C("dummy").bam(1)
fun("abc")

}
foo3(22)
})
9 changes: 9 additions & 0 deletions tests/pos/llift.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class A {
class B {
def outer(): Unit = {
def inner(): Int = 2

val fi: Function0[Int] = () => inner()
}
}
}
13 changes: 5 additions & 8 deletions tests/run/t5375.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
object Test {
object Test extends dotty.runtime.LegacyApp {
val foos = (1 to 1000).toSeq

def main(args: Array[String]): Unit = {
try
foos.par.map(i => if (i % 37 == 0) sys.error("i div 37") else i)
catch {
case ex: RuntimeException => println("Runtime exception")
}
try
foos.par.map(i => if (i % 37 == 0) sys.error("i div 37") else i)
catch {
case ex: RuntimeException => println("Runtime exception")
}
}
10 changes: 4 additions & 6 deletions tests/run/t6052.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@



object Test {
object Test extends dotty.runtime.LegacyApp {
def seqarr(i: Int) = Array[Int]() ++ (0 until i)
def pararr(i: Int) = seqarr(i).par

Expand All @@ -15,9 +15,7 @@ object Test {
assert(gseq == gpar, (gseq, gpar))
}

def main(args: Array[String]): Unit = {
for (i <- 0 until 20) check(i, _ > 0)
for (i <- 0 until 20) check(i, _ % 2)
for (i <- 0 until 20) check(i, _ % 4)
}
for (i <- 0 until 20) check(i, _ > 0)
for (i <- 0 until 20) check(i, _ % 2)
for (i <- 0 until 20) check(i, _ % 4)
}
1 change: 1 addition & 0 deletions tests/run/t6260-delambdafy.check
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
f(C@2e)

apply
get$Lambda
12 changes: 5 additions & 7 deletions tests/run/t6410.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@



object Test {
def main(args: Array[String]): Unit = {
val x = collection.parallel.mutable.ParArray.range(1,10) groupBy { _ % 2 } mapValues { _.size }
println(x)
val y = collection.parallel.immutable.ParVector.range(1,10) groupBy { _ % 2 } mapValues { _.size }
println(y)
}
object Test extends dotty.runtime.LegacyApp {
val x = collection.parallel.mutable.ParArray.range(1,10) groupBy { _ % 2 } mapValues { _.size }
println(x)
val y = collection.parallel.immutable.ParVector.range(1,10) groupBy { _ % 2 } mapValues { _.size }
println(y)
}
12 changes: 5 additions & 7 deletions tests/run/t6467.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ import collection._



object Test {
object Test extends dotty.runtime.LegacyApp {

def compare(s1: String, s2: String): Unit = {
assert(s1 == s2, s1 + "\nvs.\n" + s2)
}

def main(args: Array[String]): Unit = {
compare(List(1, 2, 3, 4).aggregate(new java.lang.StringBuffer)(_ append _, _ append _).toString, "1234")
compare(List(1, 2, 3, 4).par.aggregate(new java.lang.StringBuffer)(_ append _, _ append _).toString, "1234")
compare(Seq(0 until 100: _*).aggregate(new java.lang.StringBuffer)(_ append _, _ append _).toString, (0 until 100).mkString)
compare(Seq(0 until 100: _*).par.aggregate(new java.lang.StringBuffer)(_ append _, _ append _).toString, (0 until 100).mkString)
}
compare(List(1, 2, 3, 4).aggregate(new java.lang.StringBuffer)(_ append _, _ append _).toString, "1234")
compare(List(1, 2, 3, 4).par.aggregate(new java.lang.StringBuffer)(_ append _, _ append _).toString, "1234")
compare(Seq(0 until 100: _*).aggregate(new java.lang.StringBuffer)(_ append _, _ append _).toString, (0 until 100).mkString)
compare(Seq(0 until 100: _*).par.aggregate(new java.lang.StringBuffer)(_ append _, _ append _).toString, (0 until 100).mkString)

}
10 changes: 4 additions & 6 deletions tests/run/t7498.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@



object Test {
object Test extends dotty.runtime.LegacyApp {
import scala.collection.concurrent.TrieMap

class Collision(val idx: Int) {
override def hashCode = idx % 10
}

def main(args: Array[String]): Unit = {
val tm = TrieMap[Collision, Unit]()
for (i <- 0 until 1000) tm(new Collision(i)) = ()
val tm = TrieMap[Collision, Unit]()
for (i <- 0 until 1000) tm(new Collision(i)) = ()

tm.par.foreach(kv => ())
}
tm.par.foreach(kv => ())
}