aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala
blob: 9c01aaa9aabf4f0373dace142be44040e58c55d4 (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
package dotty.tools.dotc
package transform

import core._
import TreeTransforms._
import Contexts.Context
import Flags._
import SymUtils._
import Symbols._
import SymDenotations._
import Types._
import Decorators._
import DenotTransformers._
import Annotations._
import StdNames._
import NameOps._
import ast.Trees._

/** This phase augments Scala2 traits with implementation classes and with additional members
 *  needed for mixin composition.
 *  These symbols would have been added between Unpickling and Mixin in the Scala2 pipeline.
 *  Specifcally, it adds
 *
 *   - an implementation class which defines a trait constructor and trait method implementations
 *   - trait setters for vals defined in traits
 *
 *  Furthermore, it expands the names of all private getters and setters as well as super accessors in the trait and makes
 *  them not-private.
 */
class AugmentScala2Traits extends MiniPhaseTransform with IdentityDenotTransformer with FullParameterization { thisTransform =>
  import ast.tpd._

  override def phaseName: String = "augmentScala2Traits"

  override def rewiredTarget(referenced: Symbol, derived: Symbol)(implicit ctx: Context) = NoSymbol

  override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = {
    val cls = impl.symbol.owner.asClass
    for (mixin <- cls.mixins)
      if (mixin.is(Scala2x))
        augmentScala2Trait(mixin, cls)
    impl
  }

  private def augmentScala2Trait(mixin: ClassSymbol, cls: ClassSymbol)(implicit ctx: Context): Unit = {
    if (mixin.implClass.is(Scala2x)) () // nothing to do, mixin was already augmented
    else {
      //println(i"creating new implclass for $mixin ${mixin.implClass}")
      val ops = new MixinOps(cls, thisTransform)
      import ops._

      val implClass = ctx.newCompleteClassSymbol(
        owner = mixin.owner,
        name = mixin.name.implClassName,
        flags = Abstract | Scala2x,
        parents = defn.ObjectType :: Nil,
        assocFile = mixin.assocFile).enteredAfter(thisTransform)

      def implMethod(meth: TermSymbol): Symbol = {
        val mold =
          if (meth.isConstructor)
            meth.copySymDenotation(
              name = nme.TRAIT_CONSTRUCTOR,
              info = MethodType(Nil, defn.UnitType))
          else meth.ensureNotPrivate
        meth.copy(
          owner = implClass,
          name = mold.name.asTermName,
          flags = Method | JavaStatic | mold.flags & ExpandedName,
          info = fullyParameterizedType(mold.info, mixin))
      }

      def traitSetter(getter: TermSymbol) =
        getter.copy(
          name = getter.ensureNotPrivate.name
                  .expandedName(getter.owner, nme.TRAIT_SETTER_SEPARATOR)
                  .asTermName.setterName,
          flags = Method | Accessor | ExpandedName,
          info = MethodType(getter.info.resultType :: Nil, defn.UnitType))

      for (sym <- mixin.info.decls) {
        if (needsForwarder(sym) || sym.isConstructor || sym.isGetter && sym.is(Lazy) || sym.is(Method, butNot = Deferred))
          implClass.enter(implMethod(sym.asTerm))
        if (sym.isGetter)
          if (sym.is(Lazy)) {
            if (!sym.hasAnnotation(defn.VolatileAnnot))
              sym.addAnnotation(Annotation(defn.VolatileAnnot, Nil))
          }
          else if (!sym.is(Deferred) && !sym.setter.exists &&
                   !sym.info.resultType.isInstanceOf[ConstantType])
            traitSetter(sym.asTerm).enteredAfter(thisTransform)
        if ((sym.is(PrivateAccessor, butNot = ExpandedName) &&
          (sym.isGetter || sym.isSetter)) // strangely, Scala 2 fields are also methods that have Accessor set.
          || sym.is(SuperAccessor)) // scala2 superaccessors are pickled as private, but are compiled as public expanded
          sym.ensureNotPrivate.installAfter(thisTransform)
      }
      ctx.log(i"Scala2x trait decls of $mixin = ${mixin.info.decls.toList.map(_.showDcl)}%\n %")
      ctx.log(i"Scala2x impl decls of $mixin = ${implClass.info.decls.toList.map(_.showDcl)}%\n %")
    }
  }
}