aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/transform/PostTyper.scala
blob: 1b42a2501f39b36043c8f209220ec3c0d6585c79 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                                                                  
                             













                                                                                                  
        


                                                                                  





















                                                                                            

                                                      
























                                                                                                 

                                                  

















                                                                                      



                                                                     


                                                      



                                                              
                                    
                                                   


                                                                                            

                                                             
                                             
                                                         

















                                                                                         









                                                    
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
      }
  }
}