aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
blob: bacb88091e501d2928dd94d21ae244ed93879a59 (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
package dotty.tools.dotc
package transform

import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
import dotty.tools.dotc.ast.{Trees, tpd, untpd}
import scala.collection.{ mutable, immutable }
import ValueClasses._
import scala.annotation.tailrec
import core._
import typer.ErrorReporting._
import typer.Checking
import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._
import util.Positions._
import Decorators._
import config.Printers.typr
import Symbols._, TypeUtils._

/** A macro transform that runs immediately after typer and that performs the following functions:
 *
 *  (1) Add super accessors and protected accessors (@see SuperAccessors)
 *
 *  (2) Convert parameter fields that have the same name as a corresponding
 *      public parameter field in a superclass to a forwarder to the superclass
 *      field (corresponding = super class field is initialized with subclass field)
 *      (@see ForwardParamAccessors)
 *
 *  (3) Add synthetic methods (@see SyntheticMethods)
 *
 *  (4) Check that `New` nodes can be instantiated, and that annotations are valid
 *
 *  (5) Convert all trees representing types to TypeTrees.
 *
 *  (6) Check the bounds of AppliedTypeTrees
 *
 *  (7) Insert `.package` for selections of package object members
 *
 *  (8) Replaces self references by name with `this`
 *
 *  (9) Adds SourceFile annotations to all top-level classes and objects
 *
 *  (10) Adds Child annotations to all sealed classes
 *
 *  (11) Minimizes `call` fields of `Inline` nodes to just point to the toplevel
 *       class from which code was inlined.
 *
 *  The reason for making this a macro transform is that some functions (in particular
 *  super and protected accessors and instantiation checks) are naturally top-down and
 *  don't lend themselves to the bottom-up approach of a mini phase. The other two functions
 *  (forwarding param accessors and synthetic methods) only apply to templates and fit
 *  mini-phase or subfunction of a macro phase equally well. But taken by themselves
 *  they do not warrant their own group of miniphases before pickling.
 */
class PostTyper extends MacroTransform with IdentityDenotTransformer  { thisTransformer =>

  import tpd._

  /** the following two members override abstract members in Transform */
  override def phaseName: String = "posttyper"

  override def transformPhase(implicit ctx: Context) = thisTransformer.next

  protected def newTransformer(implicit ctx: Context): Transformer =
    new PostTyperTransformer

  val superAcc = new SuperAccessors(thisTransformer)
  val paramFwd = new ParamForwarding(thisTransformer)
  val synthMth = new SyntheticMethods(thisTransformer)

  private def newPart(tree: Tree): Option[New] = methPart(tree) match {
    case Select(nu: New, _) => Some(nu)
    case _ => None
  }

  private def checkValidJavaAnnotation(annot: Tree)(implicit ctx: Context): Unit = {
    // TODO fill in
  }

  /** If the type of `tree` is a TermRefWithSignature with an underdefined
   *  signature, narrow the type by re-computing the signature (which should
   *  be fully-defined by now).
   */
  private def fixSignature[T <: Tree](tree: T)(implicit ctx: Context): T = tree.tpe match {
    case tpe: TermRefWithSignature if tpe.signature.isUnderDefined =>
      typr.println(i"fixing $tree with type ${tree.tpe.widen.toString} with sig ${tpe.signature} to ${tpe.widen.signature}")
      tree.withType(TermRef.withSig(tpe.prefix, tpe.name, tpe.widen.signature)).asInstanceOf[T]
    case _ => tree
  }

  class PostTyperTransformer extends Transformer {

    private var inJavaAnnot: Boolean = false

    private var parentNews: Set[New] = Set()

    private def transformAnnot(annot: Tree)(implicit ctx: Context): Tree = {
      val saved = inJavaAnnot
      inJavaAnnot = annot.symbol is JavaDefined
      if (inJavaAnnot) checkValidJavaAnnotation(annot)
      try transform(annot)
      finally inJavaAnnot = saved
    }

    private def transformAnnot(annot: Annotation)(implicit ctx: Context): Annotation =
      annot.derivedAnnotation(transformAnnot(annot.tree))

    private def registerChild(sym: Symbol, tp: Type)(implicit ctx: Context) = {
      val cls = tp.classSymbol
      if (cls.is(Sealed)) cls.addAnnotation(Annotation.makeChild(sym))
    }

    private def transformMemberDef(tree: MemberDef)(implicit ctx: Context): Unit = {
      val sym = tree.symbol
      if (sym.is(CaseVal, butNot = Method | Module)) registerChild(sym, sym.info)
      sym.transformAnnotations(transformAnnot)
    }

    private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = {
      val qual = tree.qualifier
      qual.symbol.moduleClass.denot match {
        case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) =>
          transformSelect(cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name), targs)
        case _ =>
          val tree1 = super.transform(tree)
          constToLiteral(tree1) match {
            case _: Literal => tree1
            case _ => superAcc.transformSelect(tree1, targs)
          }
      }
    }

    private def normalizeTypeArgs(tree: TypeApply)(implicit ctx: Context): TypeApply = tree.tpe match {
      case pt: PolyType => // wait for more arguments coming
        tree
      case _ =>
        def decompose(tree: TypeApply): (Tree, List[Tree]) = tree.fun match {
          case fun: TypeApply =>
            val (tycon, args) = decompose(fun)
            (tycon, args ++ tree.args)
          case _ =>
            (tree.fun, tree.args)
        }
        def reorderArgs(pnames: List[Name], namedArgs: List[NamedArg], otherArgs: List[Tree]): List[Tree] = pnames match {
          case pname :: pnames1 =>
            namedArgs.partition(_.name == pname) match {
              case (NamedArg(_, arg) :: _, namedArgs1) =>
                arg :: reorderArgs(pnames1, namedArgs1, otherArgs)
              case _ =>
                val otherArg :: otherArgs1 = otherArgs
                otherArg :: reorderArgs(pnames1, namedArgs, otherArgs1)
            }
          case nil =>
            assert(namedArgs.isEmpty && otherArgs.isEmpty)
            Nil
        }
        val (tycon, args) = decompose(tree)
        tycon.tpe.widen match {
          case tp: PolyType =>
            val (namedArgs, otherArgs) = args.partition(isNamedArg)
            val args1 = reorderArgs(tp.paramNames, namedArgs.asInstanceOf[List[NamedArg]], otherArgs)
            TypeApply(tycon, args1).withPos(tree.pos).withType(tree.tpe)
          case _ =>
            tree
        }
    }

    override def transform(tree: Tree)(implicit ctx: Context): Tree =
      try tree match {
        case tree: Ident if !tree.isType =>
          tree.tpe match {
            case tpe: ThisType => This(tpe.cls).withPos(tree.pos)
            case _ => paramFwd.adaptRef(fixSignature(tree))
          }
        case tree @ Select(qual, name) =>
          if (name.isTypeName) {
            Checking.checkRealizable(qual.tpe, qual.pos.focus)
            super.transform(tree)
          }
          else
            transformSelect(paramFwd.adaptRef(fixSignature(tree)), Nil)
        case tree: Super =>
          if (ctx.owner.enclosingMethod.isInlineMethod)
            ctx.error(em"super not allowed in inline ${ctx.owner}", tree.pos)
          super.transform(tree)
        case tree: TypeApply =>
          val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree)
          Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])
          fn match {
            case sel: Select =>
              val args1 = transform(args)
              val sel1 = transformSelect(sel, args1)
              if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree1)(sel1, args1)
            case _ =>
              super.transform(tree1)
          }
        case tree @ Assign(sel: Select, _) =>
          superAcc.transformAssign(super.transform(tree))
        case Inlined(call, bindings, expansion) =>
          // Leave only a call trace consisting of
          //  - a reference to the top-level class from which the call was inlined,
          //  - the call's position
          // in the call field of an Inlined node.
          // The trace has enough info to completely reconstruct positions.
          // The minimization is done for two reasons:
          //  1. To save space (calls might contain large inline arguments, which would otherwise
          //     be duplicated
          //  2. To enable correct pickling (calls can share symbols with the inlined code, which
          //     would trigger an assertion when pickling).
          val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
          cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion))
        case tree: Template =>
          val saved = parentNews
          parentNews ++= tree.parents.flatMap(newPart)
          try {
            val templ1 = paramFwd.forwardParamAccessors(tree)
            synthMth.addSyntheticMethods(
                superAcc.wrapTemplate(templ1)(
                  super.transform(_).asInstanceOf[Template]))
          }
          finally parentNews = saved
        case tree: DefDef =>
          transformMemberDef(tree)
          superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef])
        case tree: TypeDef =>
          transformMemberDef(tree)
          val sym = tree.symbol
          if (sym.isClass) {
            // Add SourceFile annotation to top-level classes
            if (sym.owner.is(Package) &&
              ctx.compilationUnit.source.exists &&
              sym != defn.SourceFileAnnot)
              sym.addAnnotation(Annotation.makeSourceFile(ctx.compilationUnit.source.file.path))

            // Add Child annotation to sealed parents unless current class is anonymous
            if (!sym.isAnonymousClass) // ignore anonymous class
              sym.asClass.classInfo.classParents.foreach { parent =>
                val sym2 = if (sym.is(Module)) sym.sourceModule else sym
                registerChild(sym2, parent)
              }

            tree
          }
          super.transform(tree)
        case tree: MemberDef =>
          transformMemberDef(tree)
          super.transform(tree)
        case tree: New if !inJavaAnnot && !parentNews.contains(tree) =>
          Checking.checkInstantiable(tree.tpe, tree.pos)
          super.transform(tree)
        case tree @ Annotated(annotated, annot) =>
          cpy.Annotated(tree)(transform(annotated), transformAnnot(annot))
        case tree: AppliedTypeTree =>
          Checking.checkAppliedType(tree)
          super.transform(tree)
        case SingletonTypeTree(ref) =>
          Checking.checkRealizable(ref.tpe, ref.pos.focus)
          super.transform(tree)
        case tree: TypeTree =>
          tree.withType(
            tree.tpe match {
              case AnnotatedType(tpe, annot) => AnnotatedType(tpe, transformAnnot(annot))
              case tpe => tpe
            }
          )
        case Import(expr, selectors) =>
          val exprTpe = expr.tpe
          def checkIdent(ident: untpd.Ident): Unit = {
            val name = ident.name.asTermName.encode
            if (name != nme.WILDCARD && !exprTpe.member(name).exists && !exprTpe.member(name.toTypeName).exists)
              ctx.error(s"${ident.name} is not a member of ${expr.show}", ident.pos)
          }
          selectors.foreach {
            case ident: untpd.Ident                 => checkIdent(ident)
            case Thicket((ident: untpd.Ident) :: _) => checkIdent(ident)
            case _                                  =>
          }
          super.transform(tree)
        case tree =>
          super.transform(tree)
      }
      catch {
        case ex : AssertionError =>
          println(i"error while transforming $tree")
          throw ex
      }
  }
}