aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/transform/CapturedVars.scala
blob: b2118d3ec675c83e7310e96b401263991c4846c2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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 MiniPhaseTransform with IdentityDenotTransformer { thisTransform =>
  import ast.tpd._

  /** the following two members override abstract members in Transform */
  val phaseName: String = "capturedVars"

  override def treeTransformPhase = thisTransform.next

  private var captured: mutable.HashSet[Symbol] = _

  private class CollectCaptured(implicit ctx: Context) extends EnclosingMethodTraverser {
    def traverse(enclMeth: Symbol, tree: Tree) = 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) = {
      captured = mutable.HashSet()
      apply(NoSymbol, tree)
    }
  }

  override def prepareForUnit(tree: Tree)(implicit ctx: Context) = {
    (new CollectCaptured)(ctx.withPhase(thisTransform)).runOver(ctx.compilationUnit.tpdTree)
    this
  }

  /** The {Volatile|}{Int|Double|...|Object}Ref class corresponding to the class `cls`,
   *  depending on whether the reference should be @volatile
   */
  def refCls(cls: Symbol, isVolatile: Boolean)(implicit ctx: Context): Symbol = {
    val refMap = if (isVolatile) defn.volatileRefClass else defn.refClass
    refMap.getOrElse(cls, refMap(defn.ObjectClass))
  }

  def capturedType(vble: Symbol)(implicit ctx: Context): Type = {
    val oldInfo = vble.denot(ctx.withPhase(thisTransform)).info
    refCls(oldInfo.classSymbol, vble.isVolatile).typeRef
  }

  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 = refCls(sym.info.classSymbol, sym.hasAnnotation(defn.VolatileAnnot)).typeRef)
      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
  }

  override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = {
    val lhs1 = tree.lhs match {
      case TypeApply(Select(qual @ Select(qual2, nme.elem), nme.asInstanceOf_), _) =>
        assert(captured(qual2.symbol))
        qual
      case _ => tree.lhs
    }
    cpy.Assign(tree)(lhs1, tree.rhs)
  }
}