aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/transform/PostTyper.scala
blob: 1b42a2501f39b36043c8f209220ec3c0d6585c79 (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
package dotty.tools.dotc
package transform

import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
import dotty.tools.dotc.ast.{Trees, tpd}
import scala.collection.{ mutable, immutable }
import ValueClasses._
import scala.annotation.tailrec
import core._
import typer.ErrorReporting._
import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._
import util.Positions._
import Decorators._
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
 *  
 *  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)
  
  /** Check that `tp` refers to a nonAbstract class
   *  and that the instance conforms to the self type of the created class.
   */
  private def checkInstantiable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
    tp.underlyingClassRef(refinementOK = false) match {
      case tref: TypeRef =>
        val cls = tref.symbol
        if (cls.is(AbstractOrTrait))
          ctx.error(d"$cls is abstract; cannot be instantiated", pos)
        if (!cls.is(Module)) {
          val selfType = tp.givenSelfType.asSeenFrom(tref.prefix, cls.owner)
          if (selfType.exists && !(tp <:< selfType))
            ctx.error(d"$tp does not conform to its self type $selfType; cannot be instantiated")
        }
      case _ =>
    }
  
  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
  }

  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 transformAnnots(tree: MemberDef)(implicit ctx: Context): Unit =
      tree.symbol.transformAnnotations(transformAnnot)

    override def transform(tree: Tree)(implicit ctx: Context): Tree =
      try tree match {
        case impl: Template =>
          val saved = parentNews
          parentNews ++= impl.parents.flatMap(newPart)
          try 
            synthMth.addSyntheticMethods(
              paramFwd.forwardParamAccessors(
                superAcc.wrapTemplate(impl)(
                  super.transform(_).asInstanceOf[Template])))
          finally parentNews = saved
        case tree @ TypeApply(sel: Select, args) =>
          val args1 = transform(args)
          val sel1 = superAcc.transformSelect(super.transform(sel), args1)
          if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args1)
        case sel: Select =>
          superAcc.transformSelect(super.transform(sel), Nil)
        case tree @ Assign(sel: Select, _) =>
          superAcc.transformAssign(super.transform(tree))
        case tree: DefDef =>
          transformAnnots(tree)
          superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef])
        case tree: MemberDef =>
          transformAnnots(tree)
          super.transform(tree)
        case tree: New if !inJavaAnnot && !parentNews.contains(tree) =>
          checkInstantiable(tree.tpe, tree.pos)
          super.transform(tree)
        case Annotated(annot, annotated) =>
          cpy.Annotated(tree)(transformAnnot(annot), transform(annotated))
        case tree: TypeTree =>
          tree.withType(
            tree.tpe match {
              case AnnotatedType(annot, tpe) => AnnotatedType(transformAnnot(annot), tpe)
              case tpe => tpe
            }
          )
        case _ =>
          super.transform(tree)
      }
      catch {
        case ex : AssertionError =>
          println(i"error while transforming $tree")
          throw ex
      }
  }
}