diff options
-rw-r--r-- | src/compiler/scala/reflect/internal/Definitions.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/Symbols.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/opt/Inliners.scala | 7 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/TailCalls.scala | 27 | ||||
-rw-r--r-- | src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java | 1 | ||||
-rw-r--r-- | src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java | 4 | ||||
-rw-r--r-- | src/fjbg/ch/epfl/lamp/fjbg/JMethod.java | 1 | ||||
-rw-r--r-- | src/library/scala/annotation/synchronized.scala | 15 | ||||
-rw-r--r-- | test/files/run/tailcalls.check | 1 | ||||
-rw-r--r-- | test/files/run/tailcalls.scala | 13 |
11 files changed, 65 insertions, 9 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 7ed3e977db..e87f7d65b0 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -734,6 +734,7 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val ScalaStrictFPAttr = getRequiredClass("scala.annotation.strictfp") lazy val SerializableAttr = getRequiredClass("scala.annotation.serializable") // @serializable is deprecated lazy val SwitchClass = getRequiredClass("scala.annotation.switch") + lazy val SynchronizedAttr = getRequiredClass("scala.annotation.synchronized") lazy val TailrecClass = getRequiredClass("scala.annotation.tailrec") lazy val VarargsClass = getRequiredClass("scala.annotation.varargs") lazy val uncheckedStableClass = getRequiredClass("scala.annotation.unchecked.uncheckedStable") diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index ce85d65050..e997405c77 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -557,6 +557,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } def isStrictFP = hasAnnotation(ScalaStrictFPAttr) || (enclClass hasAnnotation ScalaStrictFPAttr) + def isSynchronized = hasAnnotation(SynchronizedAttr) def isSerializable = ( info.baseClasses.exists(p => p == SerializableClass || p == JavaSerializableClass) || hasAnnotation(SerializableAttr) // last part can be removed, @serializable annotation is deprecated diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index a838c7d54d..50e84a81a8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -857,6 +857,9 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with if (method.native) flags |= ACC_NATIVE + if (m.symbol.isSynchronized) + flags |= ACC_SYNCHRONIZED + jmethod = jclass.addNewMethod(flags, javaName(m.symbol), resTpe, diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index d017cf94b3..09be682123 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -706,8 +706,8 @@ abstract class Inliners extends SubComponent { if (settings.debug.value) icodes.checkValid(caller.m) } - def isStampedForInlining(stackLength: Int) = - !sameSymbols && inc.m.hasCode && shouldInline && isSafeToInline(stackLength) + def isStampedForInlining(stack: TypeStack) = + !sameSymbols && inc.m.hasCode && shouldInline && isSafeToInline(stack) && !inc.m.symbol.isSynchronized def logFailure(stackLength: Int) = log( """|inline failed for %s: @@ -724,7 +724,8 @@ abstract class Inliners extends SubComponent { def failureReason(stackLength: Int) = if (!inc.m.hasCode) "bytecode was unavailable" - else if (!isSafeToInline(stackLength)) "it is unsafe (target may reference private fields)" + else if (!isSafeToInline(stack)) "it is unsafe (target may reference private fields)" + else if (inc.m.symbol.isSynchronized) "method is synchronized" else "of a bug (run with -Ylog:inline -Ydebug for more information)" def canAccess(level: NonPublicRefs.Value) = level match { diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 23697a4730..39ca60d870 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -173,6 +173,29 @@ abstract class TailCalls extends Transform { trees map (t => transform(t, nctx)) } + /** + * Transforms methods with synchronized body into synchronized methods + */ + private def transformSynchronizedMethods(tree: Tree): Tree = { + def isSelfSynchronized(body: Apply) = body.fun match { + case TypeApply(fun @ Select(This(_), _), List(TypeTree())) + if (fun.symbol == Object_synchronized && + fun.qualifier.symbol == tree.symbol.enclClass && + !tree.symbol.enclClass.isTrait) => true + case _ => false + } + + tree match { + case DefDef(mods, name, tparams, vparamss, tpt, rhs @ Apply(_, List(body))) + if (isSelfSynchronized(rhs)) => + val res = treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, body) + res.symbol withAnnotation AnnotationInfo(SynchronizedAttr.tpe, Nil, Nil) + res + case _ => + tree + } + } + override def transform(tree: Tree): Tree = { /** A possibly polymorphic apply to be considered for tail call transformation. */ @@ -222,8 +245,8 @@ abstract class TailCalls extends Transform { else rewriteTailCall(receiver) } - tree match { - case dd @ DefDef(_, _, _, vparamss0, _, rhs0) => + transformSynchronizedMethods(tree) match { + case dd @ DefDef(mods, name, tparams, vparams, tpt, rhs) => val newCtx = new Context(dd) debuglog("Considering " + dd.name + " for tailcalls") val newRHS = transform(rhs0, newCtx) diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java b/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java index 0a48fc1628..d254acbdc1 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JAccessFlags.java @@ -19,6 +19,7 @@ public interface JAccessFlags { public static int ACC_STATIC = 0x0008; public static int ACC_FINAL = 0x0010; public static int ACC_SUPER = 0x0020; + public static int ACC_SYNCHRONIZED = 0x0020; public static int ACC_VOLATILE = 0x0040; public static int ACC_TRANSIENT = 0x0080; public static int ACC_NATIVE = 0x0100; diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java b/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java index 3d2bf87264..beb76893f6 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JFieldOrMethod.java @@ -121,6 +121,10 @@ abstract public class JFieldOrMethod extends JMember { return (accessFlags & JAccessFlags.ACC_VARARGS) != 0; } + public boolean isSynchronized() { + return (accessFlags & JAccessFlags.ACC_SYNCHRONIZED) != 0; + } + public void writeTo(DataOutputStream stream) throws IOException { if (! frozen) { try { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java b/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java index ad35c76da3..9b9da3c0aa 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JMethod.java @@ -194,6 +194,7 @@ public class JMethod extends JFieldOrMethod { else if (isNative()) buf.append("native "); if (isAbstract()) buf.append("abstract "); else if (isFinal()) buf.append("final "); + if (isSynchronized()) buf.append("synchronized "); return buf.toString(); } } diff --git a/src/library/scala/annotation/synchronized.scala b/src/library/scala/annotation/synchronized.scala new file mode 100644 index 0000000000..9470c7173c --- /dev/null +++ b/src/library/scala/annotation/synchronized.scala @@ -0,0 +1,15 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.annotation + +/** + * If this annotation is present on a method, the synchronized flag will be emitted. + * FIXME This should be internal to the compiler + */ +private[scala] class synchronized extends StaticAnnotation diff --git a/test/files/run/tailcalls.check b/test/files/run/tailcalls.check index 7670962db2..e3a70f46ae 100644 --- a/test/files/run/tailcalls.check +++ b/test/files/run/tailcalls.check @@ -43,6 +43,7 @@ test TailCall.g1 was successful test TailCall.g2 was successful test TailCall.g3 was successful test TailCall.h1 was successful +test TailCall.s1 was successful test NonTailCall.f1 0 1 2 was successful test NonTailCall.f2 was successful diff --git a/test/files/run/tailcalls.scala b/test/files/run/tailcalls.scala index 04a1a8ba19..4cf6dd46d3 100644 --- a/test/files/run/tailcalls.scala +++ b/test/files/run/tailcalls.scala @@ -182,6 +182,8 @@ class TailCall[S](s: S) { def h1(n: Int, v: Int): Int = hP(n, v); private def hP(n: Int, v: Int): Int = if (n == 0) v else hP(n - 1, v - 1); + final def s1(n: Int, v: Int): Int = synchronized { if (n == 0) v else s1(n - 1, v - 1) } + // !!! test return in non-tail-call position // !!! test non-same-instance calls // !!! test non-same-type calls @@ -229,11 +231,13 @@ class NonTailCall { Console.print(" " + n) } - final def f2(n: Int): Int = synchronized { - if (n == 0) 0 - else f2(n - 1) + final def f2(n: Int): Int = { + val next = n - 1 + synchronized { + if (n == 0) 0 + else f2(next) + } } - } //############################################################################ @@ -366,6 +370,7 @@ object Test { check_success("TailCall.g2", TailCall.g2(max, max ), 0) check_success("TailCall.g3", TailCall.g3(max, max, Nil), 0) check_success("TailCall.h1", TailCall.h1(max, max ), 0) + check_success("TailCall.s1", TailCall.s1(max, max ), 0) println val NonTailCall = new NonTailCall |