aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/transform/CapturedVars.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/transform/CapturedVars.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/transform/CapturedVars.scala149
1 files changed, 149 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala b/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala
new file mode 100644
index 000000000..cd05589c3
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala
@@ -0,0 +1,149 @@
+package dotty.tools.dotc
+package transform
+
+import TreeTransforms._
+import core.DenotTransformers._
+import core.Symbols._
+import core.Contexts._
+import core.Types._
+import core.Flags._
+import core.Decorators._
+import core.SymDenotations._
+import core.StdNames.nme
+import core.Names._
+import core.NameOps._
+import ast.Trees._
+import SymUtils._
+import collection.{ mutable, immutable }
+import collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet }
+
+class CapturedVars extends MiniPhase with IdentityDenotTransformer { thisTransform =>
+ import ast.tpd._
+
+ /** the following two members override abstract members in Transform */
+ val phaseName: String = "capturedVars"
+ val treeTransform = new Transform(Set())
+
+ private class RefInfo(implicit ctx: Context) {
+ /** The classes for which a Ref type exists. */
+ val refClassKeys: collection.Set[Symbol] =
+ defn.ScalaNumericValueClasses() + defn.BooleanClass + defn.ObjectClass
+
+ val refClass: Map[Symbol, Symbol] =
+ refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.${rc.name}Ref")).toMap
+
+ val volatileRefClass: Map[Symbol, Symbol] =
+ refClassKeys.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap
+
+ val boxedRefClasses: collection.Set[Symbol] =
+ refClassKeys.flatMap(k => Set(refClass(k), volatileRefClass(k)))
+ }
+
+ class Transform(captured: collection.Set[Symbol]) extends TreeTransform {
+ def phase = thisTransform
+
+ private var myRefInfo: RefInfo = null
+ private def refInfo(implicit ctx: Context) = {
+ if (myRefInfo == null) myRefInfo = new RefInfo()
+ myRefInfo
+ }
+
+ private class CollectCaptured(implicit ctx: Context) extends EnclosingMethodTraverser {
+ private val captured = mutable.HashSet[Symbol]()
+ def traverse(enclMeth: Symbol, tree: Tree)(implicit ctx: Context) = tree match {
+ case id: Ident =>
+ val sym = id.symbol
+ if (sym.is(Mutable, butNot = Method) && sym.owner.isTerm && sym.enclosingMethod != enclMeth) {
+ ctx.log(i"capturing $sym in ${sym.enclosingMethod}, referenced from $enclMeth")
+ captured += sym
+ }
+ case _ =>
+ foldOver(enclMeth, tree)
+ }
+ def runOver(tree: Tree): collection.Set[Symbol] = {
+ apply(NoSymbol, tree)
+ captured
+ }
+ }
+
+ override def prepareForUnit(tree: Tree)(implicit ctx: Context) = {
+ val captured = (new CollectCaptured)(ctx.withPhase(thisTransform))
+ .runOver(ctx.compilationUnit.tpdTree)
+ new Transform(captured)
+ }
+
+ /** The {Volatile|}{Int|Double|...|Object}Ref class corresponding to the class `cls`,
+ * depending on whether the reference should be @volatile
+ */
+ def refClass(cls: Symbol, isVolatile: Boolean)(implicit ctx: Context): Symbol = {
+ val refMap = if (isVolatile) refInfo.volatileRefClass else refInfo.refClass
+ if (cls.isClass) {
+ refMap.getOrElse(cls, refMap(defn.ObjectClass))
+ }
+ else refMap(defn.ObjectClass)
+ }
+
+ override def prepareForValDef(vdef: ValDef)(implicit ctx: Context) = {
+ val sym = vdef.symbol
+ if (captured contains sym) {
+ val newd = sym.denot(ctx.withPhase(thisTransform)).copySymDenotation(
+ info = refClass(sym.info.classSymbol, sym.hasAnnotation(defn.VolatileAnnot)).typeRef,
+ initFlags = sym.flags &~ Mutable)
+ newd.removeAnnotation(defn.VolatileAnnot)
+ newd.installAfter(thisTransform)
+ }
+ this
+ }
+
+ override def transformValDef(vdef: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val vble = vdef.symbol
+ if (captured contains vble) {
+ def boxMethod(name: TermName): Tree =
+ ref(vble.info.classSymbol.companionModule.info.member(name).symbol)
+ cpy.ValDef(vdef)(
+ rhs = vdef.rhs match {
+ case EmptyTree => boxMethod(nme.zero).appliedToNone.withPos(vdef.pos)
+ case arg => boxMethod(nme.create).appliedTo(arg)
+ },
+ tpt = TypeTree(vble.info).withPos(vdef.tpt.pos))
+ } else vdef
+ }
+
+ override def transformIdent(id: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val vble = id.symbol
+ if (captured(vble))
+ (id select nme.elem).ensureConforms(vble.denot(ctx.withPhase(thisTransform)).info)
+ else id
+ }
+
+ /** If assignment is to a boxed ref type, e.g.
+ *
+ * intRef.elem = expr
+ *
+ * rewrite using a temporary var to
+ *
+ * val ev$n = expr
+ * intRef.elem = ev$n
+ *
+ * That way, we avoid the problem that `expr` might contain a `try` that would
+ * run on a non-empty stack (which is illegal under JVM rules). Note that LiftTry
+ * has already run before, so such `try`s would not be eliminated.
+ *
+ * Also: If the ref type lhs is followed by a cast (can be an artifact of nested translation),
+ * drop the cast.
+ */
+ override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ def recur(lhs: Tree): Tree = lhs match {
+ case TypeApply(Select(qual, nme.asInstanceOf_), _) =>
+ val Select(_, nme.elem) = qual
+ recur(qual)
+ case Select(_, nme.elem) if refInfo.boxedRefClasses.contains(lhs.symbol.maybeOwner) =>
+ val tempDef = transformFollowing(SyntheticValDef(ctx.freshName("ev$").toTermName, tree.rhs))
+ transformFollowing(Block(tempDef :: Nil, cpy.Assign(tree)(lhs, ref(tempDef.symbol))))
+ case _ =>
+ tree
+ }
+ recur(tree.lhs)
+ }
+ }
+}